Skip to content

Commit a0c038d

Browse files
committed
OX-02: TagsChanged on the UI thread; keep the measured editor in front
The run on e71027f reached the core of the acceptance in Visual Studio 2026: the unsaved second Submit went workspace snapshot -> owen serve (extract 278 ms, core 11 ms) -> OWN002, and the Error List showed 'OWN002 | IDisposable local draft is used after it is disposed | LiveFixture | Use.cs | 10' with Use.cs still unsaved. No squiggle was traced because VS reopened its start page over the editor (an invisible view is never asked for tags). - The tagger raises TagsChanged on the UI thread and reports any exception to the trace and the Owen pane instead of letting the editor switch it off. - The driver brings Use.cs to the front before every measured edit, and reports a navigation failure as a check instead of dying silently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ZFvhLx1fM9Gerg4dKsZcL
1 parent e71027f commit a0c038d

2 files changed

Lines changed: 45 additions & 4 deletions

File tree

‎frontend/roslyn/Owen.VisualStudio/OwenErrorTagger.cs‎

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.ComponentModel.Composition;
66
using System.Diagnostics;
77
using System.Linq;
8+
using Microsoft.VisualStudio.Shell;
89
using Microsoft.VisualStudio.Text;
910
using Microsoft.VisualStudio.Text.Adornments;
1011
using Microsoft.VisualStudio.Text.Tagging;
@@ -54,11 +55,39 @@ private void OnFileChanged(string file)
5455
if (!string.Equals(System.IO.Path.GetFullPath(file), System.IO.Path.GetFullPath(_document.FilePath),
5556
StringComparison.OrdinalIgnoreCase))
5657
return;
57-
var snapshot = _buffer.CurrentSnapshot;
58-
TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, 0, snapshot.Length)));
58+
// Publications arrive on a background thread; the editor's tag aggregators expect
59+
// TagsChanged on the UI thread.
60+
_ = ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
61+
{
62+
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
63+
try
64+
{
65+
var snapshot = _buffer.CurrentSnapshot;
66+
TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, 0, snapshot.Length)));
67+
}
68+
catch (Exception ex)
69+
{
70+
OwenTrace.Event("error", new { where = "TagsChanged", error = ex.ToString() });
71+
}
72+
});
5973
}
6074

6175
public IEnumerable<ITagSpan<IErrorTag>> GetTags(NormalizedSnapshotSpanCollection spans)
76+
{
77+
try
78+
{
79+
return Tags(spans);
80+
}
81+
catch (Exception ex)
82+
{
83+
// a tagger that throws is switched off by the editor: report, show nothing this time
84+
OwenTrace.Event("error", new { where = "GetTags", error = ex.ToString() });
85+
OwenLog.Write("Owen: squiggles failed: " + ex.Message);
86+
return Array.Empty<ITagSpan<IErrorTag>>();
87+
}
88+
}
89+
90+
private IEnumerable<ITagSpan<IErrorTag>> Tags(NormalizedSnapshotSpanCollection spans)
6291
{
6392
if (spans.Count == 0)
6493
return Array.Empty<ITagSpan<IErrorTag>>();

‎scripts/vs/owen-vs-acceptance.ps1‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,14 @@ function Delete-Line($line) {
267267
$b = $td.StartPoint.CreateEditPoint(); $b.MoveToLineAndOffset($line + 1, 1)
268268
$a.Delete($b)
269269
}
270+
# VS may reopen its start page over the editor after the solution loads; an editor that is not
271+
# visible is never asked for tags, so every measured edit first brings Use.cs to the front.
272+
function Front-Use {
273+
foreach ($w in @($dte.Windows)) {
274+
try { if ($w.Caption -like "What's new*" -or $w.Caption -like "Start Page*") { $w.Close() } } catch { }
275+
}
276+
try { $script:window.Activate() } catch { Log "activate: $_" }
277+
}
270278
$stale = ' draft.Submit(now);'
271279
$anchor = 'var submitted = draft.Submit(now);'
272280
$region = 'OrderProtocol.WithDraft(order, draft =>'
@@ -295,6 +303,7 @@ else {
295303
$snap = @($script:events | Where-Object { $_.kind -eq 'snapshot' })[0]
296304
if ($snap) { Log "snapshot: documents $($snap.data.documents -join ', '); generated $($snap.data.generated -join ', ')" }
297305

306+
Front-Use
298307
$since = Now
299308
Insert-Line ((Line-Of $anchor) + 1) ' // an unsaved, harmless edit'
300309
$cleanPub = $null
@@ -308,6 +317,7 @@ Check "K1-unsaved-clean" ($cleanPub -and @($cleanPub.data.entries | Where-Object
308317
Delete-Line (Line-Of '// an unsaved, harmless edit')
309318

310319
# ---- K2: the second Submit, unsaved -----------------------------------------------------------
320+
Front-Use
311321
$since = Now
312322
$insertAt = (Line-Of $anchor) + 1
313323
Insert-Line $insertAt $stale
@@ -322,7 +332,7 @@ $row = Wait-Row 'OWN002' $true 60
322332
Check "K2-error-list" ($null -ne $row) "Error List row: $(if ($row) { $row.Text } else { 'none' })"
323333

324334
# navigation: double-click the row, then read the caret
325-
if ($row) {
335+
if ($row) { try {
326336
try { $row.Element.GetCurrentPattern([System.Windows.Automation.SelectionItemPattern]::Pattern).Select() } catch { }
327337
$r = $row.Element.Current.BoundingRectangle
328338
[Mouse]::SetForegroundWindow([IntPtr]$dte.MainWindow.HWnd) | Out-Null
@@ -333,7 +343,7 @@ if ($row) {
333343
$expectColumn = ((Retry { (All-Text) -split "`r?`n" })[$regionLine - 1]).IndexOf('OrderProtocol') + 1
334344
Check "K2-navigation" ($active.FullName -eq $fx.use -and $point.Line -eq $regionLine -and $point.LineCharOffset -eq $expectColumn) "double-click -> $($active.Name) line $($point.Line) column $($point.LineCharOffset); expected Use.cs line $regionLine column $expectColumn (the core's location, §6)"
335345
Shot "k2-navigated"
336-
}
346+
} catch { Check "K2-navigation" $false "navigation could not be driven: $($_.Exception.Message) at $($_.InvocationInfo.PositionMessage)" } }
337347

338348
# ---- P9: a CS error beside OWN002 -------------------------------------------------------------
339349
$csAt = (Line-Of $region)
@@ -347,6 +357,7 @@ Delete-Line (Line-Of 'int notAString = "s";')
347357

348358
# ---- K3: delete the line ------------------------------------------------------------------------
349359
Start-Sleep -Milliseconds 1500
360+
Front-Use
350361
$since = Now
351362
Delete-Line (Line-Of 'draft.Submit(now);')
352363
$gone = Wait-Tags $since { param($e) (Own002 $e).Count -eq 0 }
@@ -357,6 +368,7 @@ Shot "k3-gone"
357368
# ---- latency over repeated edits ---------------------------------------------------------------
358369
$appear = @(); $vanish = @()
359370
for ($i = 0; $i -lt $Repeats; $i++) {
371+
Front-Use
360372
Start-Sleep -Milliseconds 800
361373
$since = Now
362374
Insert-Line ((Line-Of $anchor) + 1) $stale

0 commit comments

Comments
 (0)