Skip to content

Roll out LSP semantic highlighting in Visual Studio - #20965

Draft
abonie wants to merge 35 commits into
mainfrom
lsp/semantic-tokens
Draft

abonie wants to merge 35 commits into
mainfrom
lsp/semantic-tokens

Conversation

@abonie

@abonie abonie commented Nov 11, 2026

Copy link
Copy Markdown
Member

Description

Integration branch for rolling out LSP-based semantic highlighting in Visual Studio. The LSP server serves textDocument/semanticTokens again, follows the active project context so highlighting stays correct after switching target frameworks, and exposes project contexts and code actions. LSP diagnostics stay off by default in favor of the existing extension. Telemetry is shared between legacy and LSP highlighting so the two paths can be compared during rollout. Also moves the F# LSP extension to VS Extensibility 18.5 / .NET 10.

Checklist

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

@github-actions

github-actions Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

✅ Release notes checked


✅ Found changes and release notes in following paths:

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.200.md

abonie and others added 18 commits November 11, 2026 11:54
- Merge LSP VSIX into the old VSIX (CreateVsixContainer, AssemblyVSIXSubPath)
- Remove unfinished semantic tokens endpoint from LanguageFeaturesHandler
- Remove SemanticTokensOptions from CapabilitiesManager and VS provider
- Remove TokenTypes module from FSharpRequestContext
- Refactor ClassificationService: simplify classification logic
- Remove unused WorkspaceExtensions and SemanticHighlighting setting
- Rename config field SemanticHighlighting -> CodeActions (default false)
- Bump LSP protocol packages to 17.13.9, add Protocol.Extensions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add ILspTelemetry abstraction with NullTelemetry default
- Rename ToLspDiagnostic -> ToVsDiagnostic with severity mapping and project info
- Add makeProjectContextId and snapshotsToProjectInfos shared helpers
- Add VSExtensionConverters to server serializer and formatter
- Expose GetProjectSnapshotsForFile in FSharpWorkspaceQuery
- Respect fsharp.getDiagnosticsFrom unified setting in DocumentDiagnosticAnalyzer
- Refactor LSP tests: extract ProtocolHelpers, add diagnostics tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add ProjectContextsHandler for the _vs_getProjectContexts LSP endpoint
- Register handler in FSharpLanguageServer DI container
- Remove duplicate project contexts handler from VsDiagnosticsHandler
- Add ProjectContextsProtocolTests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add CodeActionHandler with F# code fixes (quickfix actions)
- Register handler in FSharpLanguageServer and CapabilitiesManager
- Add CodeActionProvider capability to VS extension
- Enable CodeActions feature flag by default
- Add CodeActionProtocolTests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add VsTelemetryReporter for VS-side telemetry
- Instrument DocumentStateHandler with open/close events

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add FSharpApexTestBase for VS integration test scaffolding
- Add ErrorListDiagnosticsTests and LightBulbCodeActionTests
- Wire VsTelemetryReporter into VS extension server provider
- Fix whitespace and ordering in project files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Set the effective default of fsharp.getDiagnosticsFrom to 'old' so the
in-proc (old) extension serves diagnostics and the LSP diagnostics
endpoint stays off by default (but remains switchable to lsp/both).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Stacks the semantic-tokens-rollout work on top of the diagnostics
rollout branch: adds the textDocument/semanticTokens endpoint and
disables the LSP diagnostics endpoint (the in-proc extension stays
authoritative for diagnostics until that rollout graduates).

This reconstructs the tree of the original semantic-tokens-rollout-prep
branch (preserved as backup) on top of lsp-diagnostics-rollout.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove duplicate inline package pins, centralize LSP and Apex test dependencies, and adapt tokenizer and classification regression tests to current main APIs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: df139595-f788-456b-908f-83de4c89548c
Restore Visual Studio Extensibility 17.14.40313 and track CLaSP with the Roslyn dependency flow. Migrate LSP transports and protocol tests to System.Text.Json, adapting framework lifecycle and logger APIs while preserving VS extension metadata.

Prefer CLI event resolutions over synthesized add/remove members, add instance/static event regression coverage, and include the previously untracked project-context tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: df139595-f788-456b-908f-83de4c89548c
Forward Roslyn active document contexts to the LSP server, resolve compiler output identities, and refresh classifications without stale responses. Add alternating-framework and refresh-retry protocol coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5a0d205e-f647-4704-a3f6-1dcd028a0b44
Upgrade the LSP client to 18.12.55 and protocol packages to 18.12.5 to resolve the assembly version conflict.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a8b27f92-13b7-400d-8af8-1f85b6162e46
Align dependent packages, require Visual Studio 18.5, and update VSIX metadata paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a8b27f92-13b7-400d-8af8-1f85b6162e46
Track eligible rollout exposure and provider outcomes, apply shared consent with the Microsoft-internal override, and keep policy forwarding alive beyond document views. Add bounded startup retries, regression tests, and telemetry documentation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6449d63c-e5d8-4984-b4f5-8330252348fc
@abonie
abonie force-pushed the lsp/semantic-tokens branch from 60112fb to c236379 Compare November 11, 2026 11:07

@abonie abonie left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

🤖🕵️ AI review — verify independently.

Comment thread eng/Version.Details.xml Resolved
abonie and others added 5 commits November 11, 2026 15:22
Disable the provider before returning no connection for legacy selections so VS does not repeatedly activate it. Keep exposure telemetry but avoid per-view LSP context wiring for the legacy provider.

Retain disabled diagnostics and code-action fixtures as ignored MSTest classes, discover vstest via vswhere, and build/run the LSP integration project through the shared Apex path with its runsettings.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
Insert fun before lambda arguments, validate writable assignment targets, preserve implemented interface members and document newlines, and rename all parameter uses to match signatures. Propagate cancellation and report named code-fix failures. Disable code actions by default and opt protocol tests in explicitly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
abonie and others added 10 commits November 11, 2026 15:37
Lift synchronous fixes explicitly and keep all registrations in one flat list, including interface implementation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
…ough VMR

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
… LSP

Skip legacy semantic classification only for open host-workspace documents
when LSP is selected. Keep other workspaces, including preview and
miscellaneous files, eligible without adding a server-failure fallback.
Report fallback_only state and document the open-document FAR limitation.

Validation:
- .\Build.cmd -c Debug: passed, 0 warnings, 0 errors (after clean artifacts).
- dotnet test --project vsintegration\tests\FSharp.Editor.Tests\FSharp.Editor.Tests.fsproj -c Debug --no-build --filter-class '*Classification*' '*Telemetry*': 233 passed, 0 failed.
- Same test command with --filter-class '*SemanticClassificationPolicyTests*': 13 passed, 0 failed.
- dotnet fantomas --check on all three touched F# files: passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
Treat Host and MiscellaneousFiles as LSP-rendered editor workspaces when
open. Closed documents and other workspace kinds remain eligible for
legacy classification. Update predicate/provider tests and fallback docs.

Validation:
- .\Build.cmd -c Debug: passed, 0 warnings, 0 errors.
- dotnet test --project vsintegration\tests\FSharp.Editor.Tests\FSharp.Editor.Tests.fsproj -c Debug --no-build --filter-class '*Classification*' '*Telemetry*': 235 passed, 0 failed, 0 skipped.
- dotnet fantomas and --check on both touched F# files: passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0c106877-ba3b-418c-9716-697da9c76300

@abonie abonie left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

🤖🕵️ AI re-review — verify independently.

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(FSharpNetCoreProductTargetFramework)-windows8.0</TargetFramework>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

🤖🕵️ Every Windows job in build 1677808 fails in VerifyAssemblyVersionsAndSymbols (15 jobs, including EndToEndBuildTests and MockOfficial). AssemblyCheck requires an embedded PDB in every FSharp*.dll, and this test assembly gets DebugType=portable from tests/Directory.Build.props and isn't listed in buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt.

The following assemblies don't have an embedded pdb
D:\a\_work\1\s\artifacts\bin\FSharp.VisualStudio.Extension.Tests\Release\net11.0-windows8.0\FSharp.VisualStudio.Extension.Tests.dll
Command failed to execute with exit code 1: ... AssemblyCheck.dll

Fix: add the DLL to the skip list, as the other test projects are.

FSharp.VisualStudio.Extension.Tests.dll

Comment thread eng/tests/TestSplit.fsx
let otherProjects =
[// project path batch batched platform, unbatched platform
"tests/FSharp.Build.UnitTests/FSharp.Build.UnitTests.fsproj", 1, "all", "all"
"tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj", 1, "coreclr", "coreclr"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

🤖🕵️ Linux Batch1 and MacOS Batch1 now fail as soon as dotnet test reaches this project: global.json selects Microsoft.Testing.Platform, and the SDK classifies this project as VSTest. The project has never run in this batch before (it was excluded until f465972), so the runner setup needs fixing before it can be batched.

global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner.
The following test projects are using VSTest test runner:
FSharp.Compiler.LanguageServer.Tests.fsproj

static member ShouldClassify(provider, workspaceKind, isOpenDocument) =
let isLspEditorWorkspace =
workspaceKind = WorkspaceKind.Host
|| workspaceKind = WorkspaceKind.MiscellaneousFiles

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

🤖🕵️ Under lsp, open standalone scripts and loose .fs files lose semantic colouring. This predicate skips them, and SingleFileWorkspaceMap puts them in an "F# Miscellaneous Files" project. But the server only learns about .fsproj projects, so GetProjectSnapshotForFile returns None, GetSemanticClassification returns None, and only lexical tokens come back. The "file not in any project" protocol test only checks that the data is well-formed.

// Script.fsx opened with File > Open, not in any project
type Widget() = class end
let w = Widget()   // Widget and w get no type/value colouring; only keywords and literals

Fix: keep legacy classification for document.Project.IsFSharpMiscellaneousOrMetadata (Common/Extensions.fs:67) until the server serves script projects.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

1 participant