public async Task AddEmbeddedLanguageClassificationsAsync(
            Document document, TextSpan textSpan, ClassificationOptions options, ArrayBuilder <ClassifiedSpan> result, CancellationToken cancellationToken)
        {
            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            AddEmbeddedLanguageClassifications(document.Project, semanticModel, textSpan, options, result, cancellationToken);
        }
예제 #2
0
        internal static IEnumerable <ClassifiedSpan> GetClassifiedSpans(
            HostWorkspaceServices workspaceServices,
            SemanticModel semanticModel,
            TextSpan textSpan,
            ClassificationOptions options,
            CancellationToken cancellationToken)
        {
            var service = workspaceServices.GetLanguageServices(semanticModel.Language).GetRequiredService <ISyntaxClassificationService>();

            var syntaxClassifiers = service.GetDefaultSyntaxClassifiers();

            var extensionManager    = workspaceServices.GetRequiredService <IExtensionManager>();
            var getNodeClassifiers  = extensionManager.CreateNodeExtensionGetter(syntaxClassifiers, c => c.SyntaxNodeTypes);
            var getTokenClassifiers = extensionManager.CreateTokenExtensionGetter(syntaxClassifiers, c => c.SyntaxTokenKinds);

            using var _1 = ArrayBuilder <ClassifiedSpan> .GetInstance(out var syntacticClassifications);

            using var _2 = ArrayBuilder <ClassifiedSpan> .GetInstance(out var semanticClassifications);

            var root = semanticModel.SyntaxTree.GetRoot(cancellationToken);

            service.AddSyntacticClassifications(root, textSpan, syntacticClassifications, cancellationToken);
            service.AddSemanticClassifications(semanticModel, textSpan, getNodeClassifiers, getTokenClassifiers, semanticClassifications, options, cancellationToken);

            var allClassifications = new List <ClassifiedSpan>(semanticClassifications.Where(s => s.TextSpan.OverlapsWith(textSpan)));
            var semanticSet        = semanticClassifications.Select(s => s.TextSpan).ToSet();

            allClassifications.AddRange(syntacticClassifications.Where(
                                            s => s.TextSpan.OverlapsWith(textSpan) && !semanticSet.Contains(s.TextSpan)));
            allClassifications.Sort((s1, s2) => s1.TextSpan.Start - s2.TextSpan.Start);

            return(allClassifications);
        }
예제 #3
0
 private Task ProduceTagsAsync(
     TaggerContext <IClassificationTag> context, DocumentSnapshotSpan snapshotSpan,
     IClassificationService classificationService, ClassificationOptions options, ClassificationType type, CancellationToken cancellationToken)
 {
     return(ClassificationUtilities.ProduceTagsAsync(
                context, snapshotSpan, classificationService, _owner._typeMap, options, type, cancellationToken));
 }
 public void AddSemanticClassifications(
     SemanticModel semanticModel,
     TextSpan textSpan,
     Func <SyntaxNode, ImmutableArray <ISyntaxClassifier> > getNodeClassifiers,
     Func <SyntaxToken, ImmutableArray <ISyntaxClassifier> > getTokenClassifiers,
     ArrayBuilder <ClassifiedSpan> result,
     ClassificationOptions options,
     CancellationToken cancellationToken)
 {
     Worker.Classify(semanticModel, textSpan, result, getNodeClassifiers, getTokenClassifiers, options, cancellationToken);
 }
예제 #5
0
        public static async Task <IEnumerable <ClassifiedSpan> > GetClassifiedSpansAsync(
            Document document,
            TextSpan textSpan,
            CancellationToken cancellationToken = default)
        {
            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            var options = ClassificationOptions.From(document.Project);

            return(GetClassifiedSpans(document.Project.Solution.Workspace.Services, semanticModel, textSpan, options, cancellationToken));
        }
        public static async Task <DocumentSpan> GetClassifiedDocumentSpanAsync(
            Document document, TextSpan sourceSpan, ClassificationOptions options, CancellationToken cancellationToken)
        {
            var classifiedSpans = await ClassifyAsync(
                document, sourceSpan, options, cancellationToken).ConfigureAwait(false);

            var properties = ImmutableDictionary <string, object> .Empty.Add(
                ClassifiedSpansAndHighlightSpan.Key, classifiedSpans);

            return(new DocumentSpan(document, sourceSpan, properties));
        }
        private static async Task <ClassifiedSpansAndHighlightSpan> GetTaggedTextForDocumentRegionAsync(
            Document document, TextSpan narrowSpan, TextSpan widenedSpan, ClassificationOptions options, CancellationToken cancellationToken)
        {
            var highlightSpan = new TextSpan(
                start: narrowSpan.Start - widenedSpan.Start,
                length: narrowSpan.Length);

            var classifiedSpans = await GetClassifiedSpansAsync(
                document, narrowSpan, widenedSpan, options, cancellationToken).ConfigureAwait(false);

            return(new ClassifiedSpansAndHighlightSpan(classifiedSpans, highlightSpan));
        }
예제 #8
0
        /// <summary>
        /// Classifies the provided <paramref name="span"/> in the given <paramref name="document"/>.
        /// This will do this using an appropriate <see cref="IClassificationService"/>
        /// if that can be found.  <see cref="ImmutableArray{T}.IsDefault"/> will be returned if this
        /// fails.
        /// </summary>
        public static async Task <ImmutableArray <ClassifiedSpan> > GetClassifiedSpansAsync(
            Document document,
            TextSpan span,
            ClassificationOptions options,
            CancellationToken cancellationToken,
            bool removeAdditiveSpans      = true,
            bool fillInClassifiedSpanGaps = true)
        {
            var classificationService = document.GetLanguageService <IClassificationService>();

            if (classificationService == null)
            {
                return(default);
        public void AddEmbeddedLanguageClassifications(
            Project?project, SemanticModel semanticModel, TextSpan textSpan, ClassificationOptions options, ArrayBuilder <ClassifiedSpan> result, CancellationToken cancellationToken)
        {
            if (project is null)
            {
                return;
            }

            var root   = semanticModel.SyntaxTree.GetRoot(cancellationToken);
            var worker = new Worker(this, project, semanticModel, textSpan, options, result, cancellationToken);

            worker.Recurse(root);
        }
        private static async Task <ClassifiedSpansAndHighlightSpan> ClassifyAsync(
            Document document, TextSpan sourceSpan, ClassificationOptions options, CancellationToken cancellationToken)
        {
            var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var narrowSpan = sourceSpan;
            var lineSpan   = GetLineSpanForReference(sourceText, narrowSpan);

            var taggedLineParts = await GetTaggedTextForDocumentRegionAsync(
                document, narrowSpan, lineSpan, options, cancellationToken).ConfigureAwait(false);

            return(taggedLineParts);
        }
예제 #11
0
 internal EmbeddedLanguageClassificationContext(
     SemanticModel semanticModel,
     SyntaxToken syntaxToken,
     ClassificationOptions options,
     ArrayBuilder <ClassifiedSpan> result,
     CancellationToken cancellationToken)
 {
     SemanticModel     = semanticModel;
     SyntaxToken       = syntaxToken;
     Options           = options;
     _result           = result;
     CancellationToken = cancellationToken;
 }
        public static async Task <ClassifiedSpansAndHighlightSpan> ClassifyAsync(
            DocumentSpan documentSpan, ClassificationOptions options, CancellationToken cancellationToken)
        {
            // If the document span is providing us with the classified spans up front, then we
            // can just use that.  Otherwise, go back and actually classify the text for the line
            // the document span is on.
            if (documentSpan.Properties != null &&
                documentSpan.Properties.TryGetValue(ClassifiedSpansAndHighlightSpan.Key, out var value))
            {
                return((ClassifiedSpansAndHighlightSpan)value);
            }

            return(await ClassifyAsync(
                       documentSpan.Document, documentSpan.SourceSpan, options, cancellationToken).ConfigureAwait(false));
        }
 internal EmbeddedLanguageClassificationContext(
     Project project,
     SemanticModel semanticModel,
     SyntaxToken syntaxToken,
     ClassificationOptions options,
     IVirtualCharService virtualCharService,
     ArrayBuilder <ClassifiedSpan> result,
     CancellationToken cancellationToken)
 {
     Project            = project;
     SemanticModel      = semanticModel;
     SyntaxToken        = syntaxToken;
     Options            = options;
     VirtualCharService = virtualCharService;
     _result            = result;
     CancellationToken  = cancellationToken;
 }
예제 #14
0
        public static async Task AddClassificationsInCurrentProcessAsync(
            Document document,
            TextSpan textSpan,
            ClassificationType type,
            ClassificationOptions options,
            ArrayBuilder <ClassifiedSpan> result,
            CancellationToken cancellationToken)
        {
            if (type == ClassificationType.Semantic)
            {
                var classificationService     = document.GetRequiredLanguageService <ISyntaxClassificationService>();
                var reassignedVariableService = document.GetRequiredLanguageService <IReassignedVariableService>();

                var extensionManager = document.Project.Solution.Workspace.Services.GetRequiredService <IExtensionManager>();
                var classifiers      = classificationService.GetDefaultSyntaxClassifiers();

                var getNodeClassifiers  = extensionManager.CreateNodeExtensionGetter(classifiers, c => c.SyntaxNodeTypes);
                var getTokenClassifiers = extensionManager.CreateTokenExtensionGetter(classifiers, c => c.SyntaxTokenKinds);

                await classificationService.AddSemanticClassificationsAsync(
                    document, textSpan, options, getNodeClassifiers, getTokenClassifiers, result, cancellationToken).ConfigureAwait(false);

                if (options.ClassifyReassignedVariables)
                {
                    var reassignedVariableSpans = await reassignedVariableService.GetLocationsAsync(document, textSpan, cancellationToken).ConfigureAwait(false);

                    foreach (var span in reassignedVariableSpans)
                    {
                        result.Add(new ClassifiedSpan(span, ClassificationTypeNames.ReassignedVariable));
                    }
                }
            }
            else if (type == ClassificationType.EmbeddedLanguage)
            {
                var embeddedLanguageService = document.GetLanguageService <IEmbeddedLanguageClassificationService>();
                if (embeddedLanguageService != null)
                {
                    await embeddedLanguageService.AddEmbeddedLanguageClassificationsAsync(
                        document, textSpan, options, result, cancellationToken).ConfigureAwait(false);
                }
            }
            else
            {
                throw ExceptionUtilities.UnexpectedValue(type);
            }
        }
 public Worker(
     AbstractEmbeddedLanguageClassificationService service,
     Project project,
     SemanticModel semanticModel,
     TextSpan textSpan,
     ClassificationOptions options,
     ArrayBuilder <ClassifiedSpan> result,
     CancellationToken cancellationToken)
 {
     _owner             = service;
     _project           = project;
     _semanticModel     = semanticModel;
     _textSpan          = textSpan;
     _options           = options;
     _result            = result;
     _cancellationToken = cancellationToken;
 }
        public static async Task ProduceTagsAsync(
            TaggerContext <IClassificationTag> context,
            DocumentSnapshotSpan spanToTag,
            IClassificationService classificationService,
            ClassificationTypeMap typeMap,
            ClassificationOptions options,
            ClassificationType type,
            CancellationToken cancellationToken)
        {
            var document = spanToTag.Document;

            if (document == null)
            {
                return;
            }

            // Don't block getting classifications on building the full compilation.  This may take a significant amount
            // of time and can cause a very latency sensitive operation (copying) to block the user while we wait on this
            // work to happen.
            //
            // It's also a better experience to get classifications to the user faster versus waiting a potentially
            // large amount of time waiting for all the compilation information to be built.  For example, we can
            // classify types that we've parsed in other files, or partially loaded from metadata, even if we're still
            // parsing/loading.  For cross language projects, this also produces semantic classifications more quickly
            // as we do not have to wait on skeletons to be built.

            document = document.WithFrozenPartialSemantics(cancellationToken);
            options  = options with {
                ForceFrozenPartialSemanticsForCrossProcessOperations = true
            };

            var classified = await TryClassifyContainingMemberSpanAsync(
                context, document, spanToTag.SnapshotSpan, classificationService, typeMap, options, type, cancellationToken).ConfigureAwait(false);

            if (classified)
            {
                return;
            }

            // We weren't able to use our specialized codepaths for semantic classifying.
            // Fall back to classifying the full span that was asked for.
            await ClassifySpansAsync(
                context, document, spanToTag.SnapshotSpan, classificationService, typeMap, options, type, cancellationToken).ConfigureAwait(false);
        }
        private static async Task <ImmutableArray <ClassifiedSpan> > GetClassifiedSpansAsync(
            Document document, TextSpan narrowSpan, TextSpan widenedSpan, ClassificationOptions options, CancellationToken cancellationToken)
        {
            var result = await ClassifierHelper.GetClassifiedSpansAsync(
                document, widenedSpan, options, cancellationToken).ConfigureAwait(false);

            if (!result.IsDefault)
            {
                return(result);
            }

            // For languages that don't expose a classification service, we show the entire
            // item as plain text. Break the text into three spans so that we can properly
            // highlight the 'narrow-span' later on when we display the item.
            return(ImmutableArray.Create(
                       new ClassifiedSpan(ClassificationTypeNames.Text, TextSpan.FromBounds(widenedSpan.Start, narrowSpan.Start)),
                       new ClassifiedSpan(ClassificationTypeNames.Text, narrowSpan),
                       new ClassifiedSpan(ClassificationTypeNames.Text, TextSpan.FromBounds(narrowSpan.End, widenedSpan.End))));
        }
        public async Task AddSemanticClassificationsAsync(
            Document document,
            TextSpan textSpan,
            ClassificationOptions options,
            Func <SyntaxNode, ImmutableArray <ISyntaxClassifier> > getNodeClassifiers,
            Func <SyntaxToken, ImmutableArray <ISyntaxClassifier> > getTokenClassifiers,
            ArrayBuilder <ClassifiedSpan> result,
            CancellationToken cancellationToken)
        {
            try
            {
                var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                AddSemanticClassifications(semanticModel, textSpan, getNodeClassifiers, getTokenClassifiers, result, options, cancellationToken);
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
예제 #19
0
            private Worker(
                SemanticModel semanticModel,
                TextSpan textSpan,
                ArrayBuilder <ClassifiedSpan> list,
                Func <SyntaxNode, ImmutableArray <ISyntaxClassifier> > getNodeClassifiers,
                Func <SyntaxToken, ImmutableArray <ISyntaxClassifier> > getTokenClassifiers,
                ClassificationOptions options,
                CancellationToken cancellationToken)
            {
                _getNodeClassifiers  = getNodeClassifiers;
                _getTokenClassifiers = getTokenClassifiers;
                _semanticModel       = semanticModel;
                _syntaxTree          = semanticModel.SyntaxTree;
                _textSpan            = textSpan;
                _list = list;
                _cancellationToken = cancellationToken;
                _options           = options;

                // get one from pool
                _set          = SharedPools.Default <HashSet <ClassifiedSpan> >().AllocateAndClear();
                _pendingNodes = SharedPools.Default <Stack <SyntaxNodeOrToken> >().AllocateAndClear();
            }
예제 #20
0
            internal static void Classify(
                SemanticModel semanticModel,
                TextSpan textSpan,
                ArrayBuilder <ClassifiedSpan> list,
                Func <SyntaxNode, ImmutableArray <ISyntaxClassifier> > getNodeClassifiers,
                Func <SyntaxToken, ImmutableArray <ISyntaxClassifier> > getTokenClassifiers,
                ClassificationOptions options,
                CancellationToken cancellationToken)
            {
                var worker = new Worker(semanticModel, textSpan, list, getNodeClassifiers, getTokenClassifiers, options, cancellationToken);

                try
                {
                    worker._pendingNodes.Push(worker._syntaxTree.GetRoot(cancellationToken));
                    worker.ProcessNodes();
                }
                finally
                {
                    // release collections to the pool
                    SharedPools.Default <HashSet <ClassifiedSpan> >().ClearAndFree(worker._set);
                    SharedPools.Default <Stack <SyntaxNodeOrToken> >().ClearAndFree(worker._pendingNodes);
                }
            }
예제 #21
0
        private static async Task ClassifySpansAsync(
            TaggerContext <IClassificationTag> context,
            Document document,
            SnapshotSpan snapshotSpan,
            IClassificationService classificationService,
            ClassificationTypeMap typeMap,
            ClassificationOptions options,
            ClassificationType type,
            CancellationToken cancellationToken)
        {
            try
            {
                using (Logger.LogBlock(FunctionId.Tagger_SemanticClassification_TagProducer_ProduceTags, cancellationToken))
                {
                    using var _ = ArrayBuilder <ClassifiedSpan> .GetInstance(out var classifiedSpans);

                    await AddClassificationsAsync(
                        classificationService, options, document, snapshotSpan, classifiedSpans, type, cancellationToken).ConfigureAwait(false);

                    foreach (var span in classifiedSpans)
                    {
                        context.AddTag(Convert(typeMap, snapshotSpan.Snapshot, span));
                    }

                    var version = await document.Project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false);

                    // Let the context know that this was the span we actually tried to tag.
                    context.SetSpansTagged(ImmutableArray.Create(snapshotSpan));
                    context.State = version;
                }
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
예제 #22
0
 private static async Task AddClassificationsAsync(
     IClassificationService classificationService,
     ClassificationOptions options,
     Document document,
     SnapshotSpan snapshotSpan,
     ArrayBuilder <ClassifiedSpan> classifiedSpans,
     ClassificationType type,
     CancellationToken cancellationToken)
 {
     if (type == ClassificationType.Semantic)
     {
         await classificationService.AddSemanticClassificationsAsync(
             document, snapshotSpan.Span.ToTextSpan(), options, classifiedSpans, cancellationToken).ConfigureAwait(false);
     }
     else if (type == ClassificationType.EmbeddedLanguage)
     {
         await classificationService.AddEmbeddedLanguageClassificationsAsync(
             document, snapshotSpan.Span.ToTextSpan(), options, classifiedSpans, cancellationToken).ConfigureAwait(false);
     }
     else
     {
         throw ExceptionUtilities.UnexpectedValue(type);
     }
 }
예제 #23
0
        private static async Task <bool> TryClassifyContainingMemberSpanAsync(
            TaggerContext <IClassificationTag> context,
            Document document,
            SnapshotSpan snapshotSpan,
            IClassificationService classificationService,
            ClassificationTypeMap typeMap,
            ClassificationOptions options,
            ClassificationType type,
            CancellationToken cancellationToken)
        {
            var range = context.TextChangeRange;

            if (range == null)
            {
                // There was no text change range, we can't just reclassify a member body.
                return(false);
            }

            // there was top level edit, check whether that edit updated top level element
            if (!document.SupportsSyntaxTree)
            {
                return(false);
            }

            var lastSemanticVersion = (VersionStamp?)context.State;

            if (lastSemanticVersion != null)
            {
                var currentSemanticVersion = await document.Project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false);

                if (lastSemanticVersion.Value != currentSemanticVersion)
                {
                    // A top level change was made.  We can't perform this optimization.
                    return(false);
                }
            }

            var service = document.GetRequiredLanguageService <ISyntaxFactsService>();

            // perf optimization. Check whether all edits since the last update has happened within
            // a member. If it did, it will find the member that contains the changes and only refresh
            // that member.  If possible, try to get a speculative binder to make things even cheaper.

            var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var changedSpan = new TextSpan(range.Value.Span.Start, range.Value.NewLength);
            var member      = service.GetContainingMemberDeclaration(root, changedSpan.Start);

            if (member == null || !member.FullSpan.Contains(changedSpan))
            {
                // The edit was not fully contained in a member.  Reclassify everything.
                return(false);
            }

            var subTextSpan = service.GetMemberBodySpanForSpeculativeBinding(member);

            if (subTextSpan.IsEmpty)
            {
                // Wasn't a member we could reclassify independently.
                return(false);
            }

            var subSpanToTag = new SnapshotSpan(
                snapshotSpan.Snapshot,
                subTextSpan.Contains(changedSpan) ? subTextSpan.ToSpan() : member.FullSpan.ToSpan());

            // re-classify only the member we're inside.
            await ClassifySpansAsync(
                context, document, subSpanToTag, classificationService, typeMap, options, type, cancellationToken).ConfigureAwait(false);

            return(true);
        }
예제 #24
0
 public Task AddSemanticClassificationsAsync(
     Document document, TextSpan textSpan, ClassificationOptions options, ArrayBuilder <ClassifiedSpan> result, CancellationToken cancellationToken)
 {
     return(AddClassificationsAsync(document, textSpan, options, ClassificationType.Semantic, result, cancellationToken));
 }
예제 #25
0
        internal static async Task <ImmutableArray <SymbolDisplayPart> > GetClassifiedSymbolDisplayPartsAsync(
            HostWorkspaceServices workspaceServices, SemanticModel semanticModel, TextSpan textSpan, ClassificationOptions options,
            CancellationToken cancellationToken = default)
        {
            var classifiedSpans = GetClassifiedSpans(workspaceServices, semanticModel, textSpan, options, cancellationToken);
            var sourceText      = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false);

            return(ConvertClassificationsToParts(sourceText, textSpan.Start, classifiedSpans));
        }
예제 #26
0
 public Task AddEmbeddedLanguageClassificationsAsync(
     Document document, TextSpan textSpan, ClassificationOptions options, ArrayBuilder <ClassifiedSpan> result, CancellationToken cancellationToken)
 {
     return(AddClassificationsAsync(document, textSpan, options, ClassificationType.EmbeddedLanguage, result, cancellationToken));
 }
예제 #27
0
 public static IEnumerable <ClassifiedSpan> GetClassifiedSpans(
     SemanticModel semanticModel,
     TextSpan textSpan,
     Workspace workspace,
     CancellationToken cancellationToken = default)
 => GetClassifiedSpans(workspace.Services, semanticModel, textSpan, ClassificationOptions.From(workspace.CurrentSolution.Options, semanticModel.Language), cancellationToken);
예제 #28
0
        private static async Task AddClassificationsAsync(
            Document document,
            TextSpan textSpan,
            ClassificationOptions options,
            ClassificationType type,
            ArrayBuilder <ClassifiedSpan> result,
            CancellationToken cancellationToken)
        {
            var classificationService = document.GetLanguageService <ISyntaxClassificationService>();

            if (classificationService == null)
            {
                // When renaming a file's extension through VS when it's opened in editor,
                // the content type might change and the content type changed event can be
                // raised before the renaming propagate through VS workspace. As a result,
                // the document we got (based on the buffer) could still be the one in the workspace
                // before rename happened. This would cause us problem if the document is supported
                // by workspace but not a roslyn language (e.g. xaml, F#, etc.), since none of the roslyn
                // language services would be available.
                //
                // If this is the case, we will simply bail out. It's OK to ignore the request
                // because when the buffer eventually get associated with the correct document in roslyn
                // workspace, we will be invoked again.
                //
                // For example, if you open a xaml from from a WPF project in designer view,
                // and then rename file extension from .xaml to .cs, then the document we received
                // here would still belong to the special "-xaml" project.
                return;
            }

            var client = await RemoteHostClient.TryGetClientAsync(document.Project, cancellationToken).ConfigureAwait(false);

            if (client != null)
            {
                // We have an oop connection.  If we're not fully loaded, see if we can retrieve a previously cached set
                // of classifications from the server.  Note: this must be a separate call (instead of being part of
                // service.GetSemanticClassificationsAsync below) as we want to try to read in the cached
                // classifications without doing any syncing to the OOP process.
                var isFullyLoaded = IsFullyLoaded(document, cancellationToken);
                if (await TryGetCachedClassificationsAsync(document, textSpan, type, client, isFullyLoaded, result, cancellationToken).ConfigureAwait(false))
                {
                    return;
                }

                // Call the project overload.  Semantic classification only needs the current project's information
                // to classify properly.
                var classifiedSpans = await client.TryInvokeAsync <IRemoteSemanticClassificationService, SerializableClassifiedSpans>(
                    document.Project,
                    (service, solutionInfo, cancellationToken) => service.GetClassificationsAsync(
                        solutionInfo, document.Id, textSpan, type, options, isFullyLoaded, cancellationToken),
                    cancellationToken).ConfigureAwait(false);

                // if the remote call fails do nothing (error has already been reported)
                if (classifiedSpans.HasValue)
                {
                    classifiedSpans.Value.Rehydrate(result);
                }
            }
            else
            {
                await AddClassificationsInCurrentProcessAsync(
                    document, textSpan, type, options, result, cancellationToken).ConfigureAwait(false);
            }
        }