diff --git a/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs b/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs index 14cd1e3f60f47..dd1a9232a399e 100644 --- a/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification.Classifiers; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host; @@ -24,6 +25,9 @@ internal abstract class AbstractClassificationService(ISyntaxClassificationServi { private readonly ISyntaxClassificationService _syntaxClassificationService = syntaxClassificationService; + private Func>? _getNodeClassifiers; + private Func>? _getTokenClassifiers; + public abstract void AddLexicalClassifications(SourceText text, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken); public abstract ClassifiedSpan AdjustStaleClassification(SourceText text, ClassifiedSpan classifiedSpan); @@ -39,7 +43,7 @@ public Task AddEmbeddedLanguageClassificationsAsync( return AddClassificationsAsync(document, textSpans, options, ClassificationType.EmbeddedLanguage, result, cancellationToken); } - private static async Task AddClassificationsAsync( + public async Task AddClassificationsAsync( Document document, ImmutableArray textSpans, ClassificationOptions options, @@ -130,7 +134,7 @@ private static async Task TryGetCachedClassificationsAsync( return true; } - public static async Task AddClassificationsInCurrentProcessAsync( + private async Task AddClassificationsInCurrentProcessAsync( Document document, ImmutableArray textSpans, ClassificationType type, @@ -141,20 +145,15 @@ public static async Task AddClassificationsInCurrentProcessAsync( if (type == ClassificationType.Semantic) { var classificationService = document.GetRequiredLanguageService(); - var reassignedVariableService = document.GetRequiredLanguageService(); - var obsoleteSymbolService = document.GetRequiredLanguageService(); - - var extensionManager = document.Project.Solution.Services.GetRequiredService(); - var classifiers = classificationService.GetDefaultSyntaxClassifiers(); - var getNodeClassifiers = extensionManager.CreateNodeExtensionGetter(classifiers, c => c.SyntaxNodeTypes); - var getTokenClassifiers = extensionManager.CreateTokenExtensionGetter(classifiers, c => c.SyntaxTokenKinds); + var (getNodeClassifiers, getTokenClassifiers) = GetExtensionClassifiers(document, classificationService); await classificationService.AddSemanticClassificationsAsync( document, textSpans, options, getNodeClassifiers, getTokenClassifiers, result, cancellationToken).ConfigureAwait(false); if (options.ClassifyReassignedVariables) { + var reassignedVariableService = document.GetRequiredLanguageService(); var reassignedVariableSpans = await reassignedVariableService.GetLocationsAsync(document, textSpans, cancellationToken).ConfigureAwait(false); foreach (var span in reassignedVariableSpans) result.Add(new ClassifiedSpan(span, ClassificationTypeNames.ReassignedVariable)); @@ -162,6 +161,7 @@ await classificationService.AddSemanticClassificationsAsync( if (options.ClassifyObsoleteSymbols) { + var obsoleteSymbolService = document.GetRequiredLanguageService(); var obsoleteSymbolSpans = await obsoleteSymbolService.GetLocationsAsync(document, textSpans, cancellationToken).ConfigureAwait(false); foreach (var span in obsoleteSymbolSpans) result.Add(new ClassifiedSpan(span, ClassificationTypeNames.ObsoleteSymbol)); @@ -180,6 +180,23 @@ await embeddedLanguageService.AddEmbeddedLanguageClassificationsAsync( { throw ExceptionUtilities.UnexpectedValue(type); } + + return; + + (Func>, Func>) GetExtensionClassifiers( + Document document, ISyntaxClassificationService classificationService) + { + if (_getNodeClassifiers == null || _getTokenClassifiers == null) + { + var extensionManager = document.Project.Solution.Services.GetRequiredService(); + var classifiers = classificationService.GetDefaultSyntaxClassifiers(); + + _getNodeClassifiers = extensionManager.CreateNodeExtensionGetter(classifiers, static c => c.SyntaxNodeTypes); + _getTokenClassifiers = extensionManager.CreateTokenExtensionGetter(classifiers, static c => c.SyntaxTokenKinds); + } + + return (_getNodeClassifiers, _getTokenClassifiers); + } } public async Task AddSyntacticClassificationsAsync(Document document, ImmutableArray textSpans, SegmentedList result, CancellationToken cancellationToken) diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs index e593b1d8928de..11708587d8c9f 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -38,8 +39,12 @@ public ValueTask GetClassificationsAsync( solution = document.Project.Solution; using var _ = Classifier.GetPooledList(out var temp); - await AbstractClassificationService.AddClassificationsInCurrentProcessAsync( - document, spans, type, options, temp, cancellationToken).ConfigureAwait(false); + + // Safe to do this. The remote classification service only runs for C#/VB. So we know we'll always + // have this service and it will always be this type. + var classificationService = (AbstractClassificationService)document.GetRequiredLanguageService(); + await classificationService.AddClassificationsAsync( + document, spans, options, type, temp, cancellationToken).ConfigureAwait(false); if (isFullyLoaded) {