Skip to content

Read a script's caret without waiting for the UI thread - #20523

Open
xperiandri wants to merge 3 commits into
dotnet:mainfrom
xperiandri:fix/script-caret-deadlock
Open

Read a script's caret without waiting for the UI thread#20523
xperiandri wants to merge 3 commits into
dotnet:mainfrom
xperiandri:fix/script-caret-deadlock

Conversation

@xperiandri

@xperiandri xperiandri commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes #20522

The project options reactor looked up the caret of a script or F# Miscellaneous Files document itself: ServiceProvider.GlobalProvider, the RDT, IVsTextManager.GetActiveView, IVsTextView.GetCaretPos, and an IVsTextViewEvents connection point. That code came with #18393. All of it needs the UI thread, so when the UI thread synchronously waits on project options, as breakpoint validation does when a document frame is shown, the reactor waits for the UI thread and the UI thread waits for the reactor. The reactor is a MailboxProcessor, so its work is not joined to the UI thread's JoinableTask, and moving the lookup to the UI thread with SwitchToMainThreadAsync would hang the same way.

Now the UI thread publishes the caret and the reactor only reads it:

  • FocusedCaretTracker, an IWpfTextViewCreationListener for F# editors, keeps a FocusedCaret in each text buffer's properties. It holds the caret of whichever editor on the buffer has focus (ValueNone while none has), and raises LineChanged when the caret moves to another line or focus enters or leaves the buffer's editors. These are the same moments that OnChangeCaretLine, OnKillFocus and OnSetFocus reported before.
  • The reactor reaches the FocusedCaret through the document's SourceText container, which does not need the UI thread. It passes the caret to GetProjectOptionsFromScript and re-submits the script options on LineChanged, as it did on the text view events before.
  • Only scripts look for the caret. FCS reads it only in ScriptClosure.resolveDependencyManagerSources, to skip the #r "nuget: …" line being typed, and only scripts have those lines. A plain .fs file in F# Miscellaneous Files (the file that hung in VS hangs: project options reactor reads the caret through UI-thread COM while the UI thread waits for the reactor #20522) no longer subscribes to caret moves, and no longer recomputes script options on every caret line change.
  • TryGetIVsTextView, TryGetTextViewAndCaretPos, subscribeToTextViewEvents, TextViewEventsHandler and ConnectionPointSubscription had no other callers and are removed with the opens that only they needed.

One behaviour follows the description of #18393 more closely than its code did. Moving focus to another window now submits the #r "nuget: …" line: without focus there is no caret. Before, GetActiveView(0, …) returned the last active view whether or not it had focus, so the line stayed excluded.

Checklist

  • Test cases added. The hang needs the VS UI thread blocked in JoinableTaskFactory.Run; the editor tests have no UI thread and a null GlobalProvider, which is why they never hit it.
  • Release notes entry updated

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Change path Release notes path Description
`vsintegration/src` docs/release-notes/.VisualStudio/18.vNext.md

@github-actions github-actions Bot added the ⚠️ Affects-Design-Time Tooling check: PR touches type providers or dependency manager label Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Tooling Safety Check — Affects-Design-Time, Affects-Restore
Affects-Design-Time: Changes IDE project-options code running during document activity.
Affects-Restore: Changes handling of active #r nuget package directives.

Generated by PR Tooling Safety Check · gpt56 1.3M ·

@github-actions github-actions Bot added the ⚠️ Affects-Restore Tooling check: PR touches NuGet packages or feeds label Sep 11, 2026
xperiandri and others added 3 commits September 11, 2026 16:44
The project options reactor looked up the caret itself through
ServiceProvider.GlobalProvider, the RDT, IVsTextView and an
IVsTextViewEvents connection point, all of which need the UI thread.
When the UI thread synchronously waited on project options (breakpoint
validation when a document frame is shown), the reactor waited for the
UI thread and the UI thread for the reactor.

An IWpfTextViewCreationListener now publishes the caret of the focused
editor into the text buffer's properties, and the reactor only reads it.
Only scripts look for it: FCS uses the caret only to skip the
`#r "nuget: ..."` line being typed.

Fixes dotnet#20522

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@xperiandri
xperiandri force-pushed the fix/script-caret-deadlock branch from 89eaf2a to 550abf6 Compare September 11, 2026 14:44

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 🕵️ LGTM

@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Sep 11, 2026
@T-Gro T-Gro added the AI-reviewed PR reviewed by AI review council label Sep 11, 2026
@T-Gro
T-Gro self-requested a review September 11, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚠️ Affects-Design-Time Tooling check: PR touches type providers or dependency manager ⚠️ Affects-Restore Tooling check: PR touches NuGet packages or feeds AI-reviewed PR reviewed by AI review council

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

VS hangs: project options reactor reads the caret through UI-thread COM while the UI thread waits for the reactor

2 participants