Skip to content

Switch to balanced source generator execution in VSCode with refresh support#82330

Merged
dibarbet merged 2 commits intodotnet:mainfrom
dibarbet:balanced_vscode
Feb 9, 2026
Merged

Switch to balanced source generator execution in VSCode with refresh support#82330
dibarbet merged 2 commits intodotnet:mainfrom
dibarbet:balanced_vscode

Conversation

@dibarbet
Copy link
Copy Markdown
Member

@dibarbet dibarbet commented Feb 7, 2026

Client side PR: dotnet/vscode-csharp#8970

In VS we switched to balanced source gen execution a while back (#73618) to avoid perf issues with running generators on every keystroke. This ports the same change to VSCode.

Also added a small benchmark to compare perf between balanced and automatic. Balanced shows an improvement in cpu and allocations, even with an extremely simple generator.

Method TypingIterations ExecutionPreference Mean Error Gen 0 Gen 1 Gen 2 Allocated
TypeAndWaitForSourceGenerators 50 Automatic 243.8 ms NA - - - 11 MB
TypeAndWaitForSourceGenerators 50 Balanced 239.9 ms NA - - - 10 MB
TypeAndWaitForSourceGenerators 1000 Automatic 4,345.3 ms NA 2000.0000 1000.0000 - 216 MB
TypeAndWaitForSourceGenerators 1000 Balanced 3,897.0 ms NA 2000.0000 1000.0000 - 186 MB

@dibarbet dibarbet marked this pull request as ready for review February 7, 2026 04:41
@dibarbet dibarbet requested a review from a team as a code owner February 7, 2026 04:41
Description = "Controls when source generators are executed.",
Required = false,
// Balanced mode requires additional client side support (to trigger refreshes), so by default run in automatic to ensure tool scenarios without client support run generators.
DefaultValueFactory = _ => SourceGeneratorExecutionPreference.Automatic,
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.

this seemed like a sane default if not provided to allow tools to work more easily. In VSCode we explicitly pass this and pick Balanced as the default there

DefaultValueFactory = _ => false,
};

var sourceGeneratorExecutionOption = new Option<SourceGeneratorExecutionPreference>("--sourceGeneratorExecutionPreference")
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.

While we do have support live option changes, source generator execution preference does not support live modification, so this is passed in as a CLI arg.

@dibarbet dibarbet merged commit 6f3ed43 into dotnet:main Feb 9, 2026
25 checks passed
@dibarbet dibarbet deleted the balanced_vscode branch February 9, 2026 18:17
@dotnet-policy-service dotnet-policy-service Bot added this to the Next milestone Feb 9, 2026
[Method(Methods.TextDocumentDidSaveName)]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class DidSaveHandler() : ILspServiceNotificationHandler<DidSaveTextDocumentParams>, ITextDocumentIdentifierHandler<DidSaveTextDocumentParams, TextDocumentIdentifier>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

sealed?

[Method(MethodName)]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class WorkspaceRefreshSourceGeneratorsHandler(LspWorkspaceRegistrationService workspaceRegistrationService) : ILspServiceNotificationHandler<RefreshSourceGeneratorsParams>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

sealed

[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal class WorkspaceRefreshSourceGeneratorsHandler(LspWorkspaceRegistrationService workspaceRegistrationService) : ILspServiceNotificationHandler<RefreshSourceGeneratorsParams>
{
public const string MethodName = "workspace/_roslyn_refreshSourceGenerators";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

does this need to be public? (maybe it's tests using this. if so, that's fine).

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.

yes, for tests

}

[Theory, CombinatorialData]
internal async Task TestSaveRefreshesSourceGenerators(bool mutatingLspWorkspace, SourceGeneratorExecutionPreference sourceGeneratorExecution)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

odd that this is internal. but don't care.

using Roslyn.Test.Utilities;
using Xunit;
using LSP = Roslyn.LanguageServer.Protocol;
using SumType = Roslyn.LanguageServer.Protocol.SumType<Roslyn.LanguageServer.Protocol.FullDocumentDiagnosticReport, Roslyn.LanguageServer.Protocol.UnchangedDocumentDiagnosticReport>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit. if you move aliases into the namespace, you cn then use your outer aliases like LSP.


public LspSourceGeneratorBenchmarks() : base(null)
{
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: primary constructor.

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

It might be good to look into, or consider a tracking issue, about triggering this on build, not just save.

@dibarbet
Copy link
Copy Markdown
Member Author

dibarbet commented Feb 9, 2026

It might be good to look into, or consider a tracking issue, about triggering this on build, not just save.

this is done already - https://github.com/dotnet/vscode-csharp/pull/8970/changes#diff-6b492de5f696ddcacaa4b8d6d23e6ac05715105d61be575e464f279fe01ca6c5R39

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants