Skip to content

Keep collapsed regions while a project has no options - #20451

Open
xperiandri wants to merge 3 commits into
dotnet:mainfrom
xperiandri:outlining-quick-parsing-options
Open

Keep collapsed regions while a project has no options#20451
xperiandri wants to merge 3 commits into
dotnet:mainfrom
xperiandri:outlining-quick-parsing-options

Conversation

@xperiandri

Copy link
Copy Markdown
Contributor

Description

Outlining needs a parse and nothing else, but FSharpBlockStructureService asked for the parse of a fully configured project and answered with no regions whenever that was unavailable — while a project loads or reloads, and for any cancellation the checker relabels with the caller's token. Roslyn takes "no regions" literally and expands every region the user had folded.

It now parses with the quick parsing options in that case, the same ones syntactic classification and brace matching already use, so the folds survive a project reload. Those options carried no source files, which made ParseFile throw looking for the last compiland, so they now carry the file being parsed.

Depends on #20450 — it is the head of this branch and supplies Document.TryGetFSharpParseResultsAsync. Only the last commit, "Outline with the quick parsing options when a project has none", belongs to this pull request; please review it after #20450 merges.

Checklist

  • Test cases added
  • Performance benchmarks added in case of performance changes
  • Release notes entry updated

@github-actions

github-actions Bot commented Sep 4, 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

@xperiandri
xperiandri force-pushed the outlining-quick-parsing-options branch from 16b46f2 to 1d1ee34 Compare September 4, 2026 17:30
@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label Sep 4, 2026
xperiandri and others added 3 commits September 5, 2026 00:10
Every entry point in WorkspaceExtensions signalled "the project has no
options yet" by raising an OperationCanceledException, which the callers
then had to tell apart from a real cancellation. They cannot, so they
treat both as "cancelled" and return an empty result. For a service
Roslyn asks repeatedly while a solution loads, that is a wrong answer,
not a missing one.

Add `Try` siblings that return ValueNone instead, and keep the raising
members as thin wrappers so the call sites that have not moved over
still get the same exception with the same message.

The four-tuple those members hand out becomes a named record, which is
also what ProjectCache stores, so a cache hit hands back the instance it
holds rather than rebuilding a tuple.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Splitting the semantic classification cache in two (dotnet#15954) left the
open-document branch reading the opened-documents cache and writing the
unopened one, so the opened cache was never populated and every request
for an open file re-ran the checker. What it wrote was also only the
requested span, keyed by document and text version, so a second request
for another span of the same version (scrolling, a split view, Roslyn
asking around the viewport) would have hit that entry and sliced nothing
out of it.

Cache the classification of the whole file instead, and slice it per
request, the way the unopened-documents branch already does.

A check that cannot complete - the project is loading or reloading, or
the check was superseded - must not answer with no classifications
either: Roslyn replaces the tags of a span with whatever comes back, so
an empty answer strips the colours the user is looking at, while a
cancellation carrying Roslyn's own token leaves them alone. Keep the
last classification per document and re-emit it for those requests, but
only while the text it was computed from is still the current one, as
its spans would otherwise land on the wrong characters.

`ifCanceledThen` replaces `ifCanceledReturn ()` for the same reason: the
checker relabels its internal cancellations with the caller's token, and
those must produce the last known colours rather than none.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Outlining needs a parse, nothing more, but it asked for the parse of a
fully configured project and answered with no regions whenever that was
unavailable - while the project loads or reloads, and for any
cancellation the checker relabelled with the caller's token. Roslyn
takes that literally and expands every region the user had folded.

Parse with the quick parsing options in that case, the ones syntactic
classification and brace matching already use, and keep the regions.

Those options carried no source files, so `ParseFile` threw looking for
the last compiland; give them the file being parsed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@xperiandri
xperiandri force-pushed the outlining-quick-parsing-options branch from 1d1ee34 to f41f3e4 Compare September 4, 2026 22:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

1 participant