diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/SemanticTokens/CohostSemanticTokensRangeEndpoint.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/SemanticTokens/CohostSemanticTokensRangeEndpoint.cs index 1a3a14bec25..edccbc6fb50 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/SemanticTokens/CohostSemanticTokensRangeEndpoint.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/SemanticTokens/CohostSemanticTokensRangeEndpoint.cs @@ -39,6 +39,23 @@ internal sealed class CohostSemanticTokensRangeEndpoint( protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(SemanticTokensRangeParams request) => request.TextDocument.ToRazorTextDocumentIdentifier(); + protected override async Task HandleRequestAsync(SemanticTokensRangeParams request, RazorCohostRequestContext context, TextDocument razorDocument, CancellationToken cancellationToken) + { + var result = await HandleRequestAsync(request, razorDocument, cancellationToken).ConfigureAwait(false); + + if (result is not null) + { + // Roslyn uses frozen semantics for semantic tokens, so it could return results from an older project state. + // Every time they get a request they queue up a refresh, which will check the project checksums, and if there + // hasn't been any changes, will no-op. We call into that same logic here to ensure everything is up to date. + // See: https://github.com/dotnet/roslyn/blob/bb57f4643bb3d52eb7626f9863da177d9e219f1e/src/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs#L48-L52 + var semanticTokensWrapperService = context.GetRequiredService(); + await semanticTokensWrapperService.TryEnqueueRefreshComputationAsync(razorDocument.Project, cancellationToken).ConfigureAwait(false); + } + + return result; + } + protected override Task HandleRequestAsync(SemanticTokensRangeParams request, TextDocument razorDocument, CancellationToken cancellationToken) => HandleRequestAsync(razorDocument, request.Range.ToLinePositionSpan(), cancellationToken);