Add fine-grained debug steps with highlighting for C# and ILAst#3839
Open
siegfriedpammer wants to merge 13 commits into
Open
Add fine-grained debug steps with highlighting for C# and ILAst#3839siegfriedpammer wants to merge 13 commits into
siegfriedpammer wants to merge 13 commits into
Conversation
925acb3 to
53cf7d0
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR upgrades ILSpy’s Debug Steps feature to support fine-grained, navigable mutation steps for both the C# AST and ILAst views, with automatic highlight + scroll-to-change after replaying a selected step (“before/after”).
Changes:
- Adds step-level replay support by threading a
HighlightStepthrough decompilation requests and outputs. - Introduces language-agnostic node-to-text range tracking (
INodeTrackingOutput+NodeLookup) and a shared step-to-range resolver (DebugStepHighlighter). - Instruments C# and ILAst rendering/transform pipelines to record step metadata and node spans; adds headless UI tests.
Reviewed changes
Copilot reviewed 97 out of 97 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| ILSpy/ViewModels/DebugStepsPaneModel.cs | Passes highlightStep through debug-step replay commands. |
| ILSpy/TextView/TextRange.cs | Adds a lightweight (Start, Length) range type for highlighting. |
| ILSpy/TextView/NodeLookup.cs | Adds object→text-range map (incl. AST annotations) used for highlight resolution. |
| ILSpy/TextView/DecompilerTextView.axaml.cs | Adds debug-step marker rendering and centering behavior in the editor. |
| ILSpy/TextView/DecompilerTabPageModel.cs | Threads highlight state (DebugStepHighlight) through decompile lifecycle and tab model. |
| ILSpy/TextView/DebugStepHighlighter.cs | Resolves a stepper step to a TextRange via NodeLookup. |
| ILSpy/TextView/AvaloniaEditTextOutput.cs | Implements INodeTrackingOutput; collects node spans and debug highlight output. |
| ILSpy/Languages/ILAstLanguage.cs | Applies step highlight resolution for ILAst output runs. |
| ILSpy/Languages/CSharpLanguage.DebugSteps.cs | Switches C# stepper source to decompiler-provided stepper on full runs. |
| ILSpy/Languages/CSharpLanguage.cs | Wires stepper limits into C# decompiler and emits highlight range after rendering. |
| ILSpy/Languages/CSharpHighlightingTokenWriter.cs | Records AST node spans during token writing for later highlight lookup. |
| ILSpy/DecompilationOptions.cs | Adds HighlightStep option to distinguish “stop point” from “highlighted step.” |
| ILSpy.Tests/Views/DebugStepsTests.cs | Adds headless UI tests for step grouping and highlight resolution (C# + ILAst). |
| ICSharpCode.Decompiler/Output/INodeTrackingOutput.cs | Introduces optional node-span tracking API + extension helpers. |
| ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/UserDefinedLogicTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/Stepper.cs | Adds modified-node tracking fields, limit bookkeeping, and GetStepByBeginStep. |
| ICSharpCode.Decompiler/IL/Transforms/RemoveUnconstrainedGenericReferenceTypeCheck.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/RemoveInfeasiblePathTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/LockTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs | Returns produced call node so callers can EndStep accurately. |
| ICSharpCode.Decompiler/IL/Transforms/LdLocaDupInitObjTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/InterpolatedStringTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/IndexRangeTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs | Adds EndStep helper for IL transforms to prefer produced nodes. |
| ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/DetectCatchWhenConditionBlocks.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs | Records produced-node via EndStep for accurate highlight targeting. |
| ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/StringToInt.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/MemoryInstructions.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/Leave.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/LdLen.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/ExpressionTreeCast.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/Conv.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/Comp.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/Branch.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/Block.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs | Wraps IL instruction writing with node-span tracking calls. |
| ICSharpCode.Decompiler/IL/Instructions.tt | Updates T4 template to auto-wrap generated WriteTo with node-span tracking. |
| ICSharpCode.Decompiler/IL/Instructions.cs | Regenerated IL instruction WriteTo methods to include node-span tracking. |
| ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs | Adds per-mutation step recording in the C# AST pipeline. |
| ICSharpCode.Decompiler/CSharp/Transforms/TransformContext.cs | Adds STEP-only Step/EndStep helpers and node-candidate tracking for AST steps. |
| ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs | Adds per-mutation step recording in the C# AST pipeline. |
| ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs | Adds per-mutation step recording in the C# AST pipeline. |
| ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs | Adds per-mutation step recording in the C# AST pipeline. |
| ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs | Adds per-mutation step recording in the C# AST pipeline. |
| ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs | Adds step recording around ambiguous-type qualification and using insertion. |
| ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs | Adds step recording for unsafe modifier / pointer syntax transforms. |
| ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs | Adds step recording for query-expression synthesis and rewrites. |
| ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs | Adds step recording for extension-method call rewriting. |
| ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs | Adds step recording for switch block flattening. |
| ICSharpCode.Decompiler/CSharp/Transforms/FixNameCollisions.cs | Adds step recording for rename fixes. |
| ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs | Adds step recording for identifier escaping. |
| ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs | Adds step recording for variable declaration insertion and rewrites. |
| ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs | Adds step recording for query continuation and identifier removal. |
| ICSharpCode.Decompiler/CSharp/Transforms/AddXmlDocumentationTransform.cs | Adds step recording for XML doc insertion. |
| ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs | Adds step recording when inserting checked/unchecked blocks/expressions. |
| ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs | Adds AST transform groups + step-limit support via shared Stepper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+105
to
+113
| [Conditional("STEP")] | ||
| internal void EndStep(AstNode? modifiedNode) | ||
| { | ||
| if (Stepper.LastStep != null) | ||
| { | ||
| Stepper.LastStep.ModifiedNode = modifiedNode; | ||
| TrackModifiedNode(Stepper.LastStep, modifiedNode, insertFirst: true); | ||
| } | ||
| } |
Comment on lines
+961
to
+964
| Editor.TextArea.Caret.Offset = start; | ||
| Editor.TextArea.Caret.BringCaretToView(); | ||
| Dispatcher.UIThread.Post(() => CenterDocumentOffsetInView(start)); | ||
| CaretHighlightAdorner.DisplayCaretHighlightAnimation(Editor.TextArea); |
Comment on lines
+292
to
+300
| public void MarkNodeEnd(object node) | ||
| { | ||
| if (openNodes.Count == 0) | ||
| return; | ||
| var (currentNode, start) = openNodes.Pop(); | ||
| if (!ReferenceEquals(currentNode, node)) | ||
| return; | ||
| NodeLookup.AddNode(node, start, builder.Length - start); | ||
| } |
Comment on lines
532
to
541
| var output = new AvaloniaEditTextOutput { LengthLimit = outputLengthLimit }; | ||
| // decompilerSettings is null only in design-time / minimal test hosts | ||
| // without composition; fall back to defaults there. | ||
| var options = new DecompilationOptions( | ||
| decompilerSettings ?? new ICSharpCode.Decompiler.DecompilerSettings()) { | ||
| CancellationToken = cts.Token, | ||
| StepLimit = stepLimit, | ||
| HighlightStep = highlightStep, | ||
| IsDebug = isDebug, | ||
| }; |
fb820cd to
b40936a
Compare
Record AST transform groups and mutation steps through the C# pipeline, replay selected steps with the stepper, and carry modified-node ranges through output so the Debug Steps pane can highlight the selected mutation without replacing its full step tree. Assisted-by: CodeAlta:gpt-5.5:CodeAlta
Place the selected debug-step node in the middle of the editor after replay so navigation lands with enough surrounding context instead of just barely scrolling the mark into view. Assisted-by: CodeAlta:gpt-5.5:CodeAlta
The C# debug-steps view highlights and centers the exact AST node a transform changed; the ILAst view already had the step tree and replay-at-step but produced no highlight. Bring it to parity. IL rendering has no token-writer seam like the C# output visitor, so per-instruction text spans are recorded by bracketing ILInstruction.WriteTo via a new INodeTrackingOutput. The dominant inst.ReplaceWith(newInst) transform pattern detaches the instruction passed to Step, so ILTransformContext gains EndStep to record the produced instruction; Stepper additionally records the position's ancestor chain as fallback candidates before the step-limit throw, so the "show state before" view -- which halts at the selected step -- still resolves to a surviving ancestor (ultimately the ILFunction). The highlight-range resolver is shared with the C# language. Assisted-by: Claude:claude-opus-4-8:Claude Code
The Debug Steps "show state before/after" replay re-decompiles the active tab; the headless UI tests inferred completion by polling IsDecompiling + Text, which can return on stale state or hit the 60s wait deadline when that shared signal races under CI load (the observed intermittent CI timeout). RestartDecompileWithStepLimit now returns the decompile Task so the replay completion can be awaited deterministically; the tests await it instead of polling. The production IsDecompiling reset is unchanged -- the last decompile's finally always resets it; the race was only in the test's completion inference. Assisted-by: Claude:claude-opus-4-8:Claude Code
MarkNodeStart and BeginSpan captured builder.Length to anchor a node range or highlight span, but indentation is written lazily on the first token of a line. A node or span opened at the start of an indented line therefore recorded its start before the leading tabs, so the debug-step highlight (and any span) extended back across the indentation to column 0. Flush the pending indent in both before capturing the offset, matching the WPF AvalonEditTextOutput.BeginSpan the Avalonia port derived from. The emitted text is unchanged -- the indent is written either way, in the same place; only the recorded start moves to the first real character. Assisted-by: Claude:claude-opus-4-8:Claude Code
The debug-step highlight centred with a logical (line-number * line-height) calculation posted at default dispatcher priority. That ignores collapsed foldings above the target, races the layout of a just-applied document (silently no-oping when the viewport is not yet measured), and does not recheck the document, so a newer decompile could scroll the wrong content. The symptom was unreliable centring, most visible in the ILAst view whose nested block output is fold-dense and larger. Route the highlight through the existing CenterLineInView helper that bookmark navigation already uses: it centres via GetVisualTopByDocumentLine (fold-aware), runs at Background priority so the document finishes measuring first, and rechecks the document identity. Assisted-by: Claude:claude-opus-4-8:Claude Code
NodeLookup population runs per AST node (and per annotation) on every on-screen C# decompile, but the debug-step highlighter only consumes it when a step limit is set. Gate the node-tracking token writer on StepLimit so the common Release path skips the bookkeeping; AvaloniaEditTextOutput is an ISmartTextOutput, so it still gets full syntax highlighting via the existing branch. Assisted-by: Claude:claude-opus-4-8:Claude Code
A step that removes a node has nothing left to highlight in the resulting text, so range resolution fell back to the enclosing block and flooded it. Record the changed node's surviving neighbours as seam anchors (captured before the mutation) and split a step's candidates into precise / seam / ancestor tiers: when neither the node nor its marker resolves, place a zero-length caret at the gap -- the successor's start, else the predecessor's end -- and only fall back to the enclosing block when no neighbour survives. A zero-length highlight is rendered as a caret (positioned, pulsed, centered) with no background mark. Assisted-by: Claude:claude-opus-4-8:Claude Code
The step tree can run to hundreds of entries per decompile, so finding a specific mutation means scrolling. Add a filter box in the pane's top-right corner: a row survives when its description -- or any descendant's -- contains the text (case-insensitive), keeping the path to every match, and the tree auto-expands while filtering so matches nested under transform groups stay visible. Implemented as an item-visibility converter over the existing TreeView rather than switching to SharpTreeView, which would change the Steps contract and rewrite the pane's tests for no functional gain here. Assisted-by: Claude:claude-opus-4-8:Claude Code
The debug-step node bracket (MarkNodeStart/try/finally/MarkNodeEnd) was copied into every hand-written WriteTo override and re-emitted by the T4 generator, so a newly added instruction could silently omit it and lose step highlighting with no compile error or test failure. Seal WriteTo on ILInstruction to apply the bracket once and delegate to a new abstract WriteToCore; move every override (hand-written and generated) to WriteToCore without the wrapper. Rendered output is unchanged -- the marks are no-ops unless the output tracks nodes. Assisted-by: Claude:claude-opus-4-8:Claude Code
NodeLookup.AddNode indexed every annotation of every rendered node by reference identity, but the debug-step highlighter only ever looks up the DebugStepMarker; the rest were dead keys, and a shared annotation (ResolveResult and friends, copied across nodes) would resolve to whichever node rendered last. Make DebugStepMarker public and bridge only it -- behaviour-preserving for resolution while dropping the per-annotation dictionary churn on every rendered node. Assisted-by: Claude:claude-opus-4-8:Claude Code
When the step limit falls on a C# transform step, Stepper.Step records the node as LimitReachedStep but throws before TransformContext can attach the node's highlight candidates, so the 'show state before' view had only the bare modified node to resolve against -- and nothing if that node renders no text of its own. The IL path already records its candidates before the throw; mirror that on the C# side by attaching the candidates to the limit-reached node in the catch, then re-throwing so the pipeline still halts. Assisted-by: Claude:claude-opus-4-8:Claude Code
The replay tests only checked DebugStepHighlight was non-null, which the ancestor fallback satisfies unconditionally -- a regression widening every highlight to the enclosing method would have passed. Assert instead that the range lies in bounds, does not span the whole document, and (unless it is a zero-length removal caret) covers non-whitespace rendered code. Assisted-by: Claude:claude-opus-4-8:Claude Code
b40936a to
954ab83
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Makes the Debug Steps pane fine-grained and navigable for both the C# AST and ILAst decompiler views. Previously the pane listed one entry per transform; now it shows the individual mutation steps inside each transform, grouped by transform, with a filter box in the top-right corner to narrow the tree by matching text. Selecting a step (or "Show state before/after") re-decompiles at that point and highlights and scrolls to the exact node/instruction the transform changed (amber marker, centered in the view). A step that removes a node drops a thin caret at the gap it left, rather than flooding the enclosing block.
How
TransformContext): each transform records fine-grainedStep/EndStepcalls; the changed node is mapped to its rendered text span via the highlighting token writer +NodeLookup.ILTransformContext): the sameStep/EndSteptwo-method API. IL rendering has no token-writer seam like the C# output visitor, so per-instruction text spans are recorded by bracketing rendering through the optionalINodeTrackingOutput. The bracket lives in exactly one place:ILInstruction.WriteTois sealed to wrap a newprotected abstract WriteToCore(the override point), so a newly added instruction cannot forget it — the T4 generator and every hand-written override now emitWriteToCore.NodeLookupbridges only the debug-step marker to a text range, not every annotation. Both the C# and IL paths attach the changed node's fallback candidates to the step even when it is the one that hits the step limit, so "show state before" (which halts at the selected step) still resolves.CenterLineInView/GetVisualTopByDocumentLine, background dispatcher priority, document re-check), so it lands correctly even with collapsed foldings — dense in the ILAst block output.Tests
Headless UI tests cover both languages: the changed node/instruction is located for both the "before" and "after" replay of a selected step (asserting a precise, in-bounds, non-flooding range — not merely non-null); a removal step resolves to a zero-length seam caret; the filter keeps matches and the path to them; and
NodeLookupbridges only the debug-step marker. The decompiler engine builds clean in Debug (withSTEP) and Release (the[Conditional("STEP")]step calls compile out with no unused-local fallout).🤖 Generated with Claude Code