public static async Task ProduceTagsAsync <TClassificationService>(
            TaggerContext <IClassificationTag> context,
            DocumentSnapshotSpan spanToTag,
            IClassificationDelegationService <TClassificationService> delegationService,
            TClassificationService classificationService,
            ClassificationTypeMap typeMap)
        {
            var document = spanToTag.Document;

            if (document == null)
            {
                return;
            }

            var classified = await TryClassifyContainingMemberSpan(
                context, spanToTag, delegationService, classificationService, typeMap).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, spanToTag, delegationService, classificationService, typeMap).ConfigureAwait(false);
        }
 private void AddClassifiedSpansForTokens <TClassificationService>(
     IClassificationDelegationService <TClassificationService> delegationService,
     TClassificationService classificationService,
     SnapshotSpan span,
     List <ClassifiedSpan> classifiedSpans)
 {
     delegationService.AddLexicalClassifications(
         classificationService, span.Snapshot.AsText(), span.Span.ToTextSpan(), classifiedSpans, CancellationToken.None);
 }
        private static async Task <ImmutableArray <ClassifiedSpan> > GetClassifiedSpansAsync <TClassificationService>(
            Document document, TextSpan narrowSpan, TextSpan widenedSpan,
            IClassificationDelegationService <TClassificationService> delegationService,
            CancellationToken cancellationToken) where TClassificationService : class, ILanguageService
        {
            var classificationService = document.GetLanguageService <TClassificationService>();

            if (classificationService == null)
            {
                return(default);
Пример #4
0
            private Task ProduceTagsAsync <TClassificationService>(
                TaggerContext <IClassificationTag> context,
                DocumentSnapshotSpan documentSpan,
                ClassificationTypeMap typeMap,
                IClassificationDelegationService <TClassificationService> delegationService) where TClassificationService : class, ILanguageService
            {
                var document = documentSpan.Document;

                var classificationService = document.GetLanguageService <TClassificationService>();

                if (classificationService != null)
                {
                    return(SemanticClassificationUtilities.ProduceTagsAsync(
                               context, documentSpan, delegationService, classificationService, typeMap));
                }

                return(SpecializedTasks.EmptyTask);
            }
            private void AddClassifiedSpans <TClassificationService>(
                IClassificationDelegationService <TClassificationService> delegationService,
                TClassificationService classificationService,
                SnapshotSpan span,
                List <ClassifiedSpan> classifiedSpans)
            {
                // First, get the tree and snapshot that we'll be operating over.
                // From this point on we'll do all operations over these values.
                ITextSnapshot lastSnapshot;
                Document      lastDocument;

                lock (_gate)
                {
                    lastSnapshot = _lastProcessedSnapshot;
                    lastDocument = _lastProcessedDocument;
                }

                if (lastDocument == null)
                {
                    // We don't have a syntax tree yet.  Just do a lexical classification of the document.
                    AddClassifiedSpansForTokens(delegationService, classificationService, span, classifiedSpans);
                    return;
                }

                // We have a tree.  However, the tree may be for an older version of the snapshot.
                // If it is for an older version, then classify that older version and translate
                // the classifications forward.  Otherwise, just classify normally.

                if (lastSnapshot.Version.ReiteratedVersionNumber == span.Snapshot.Version.ReiteratedVersionNumber)
                {
                    AddClassifiedSpansForCurrentTree(
                        delegationService, classificationService, span, lastDocument, classifiedSpans);
                }
                else
                {
                    // Slightly more complicated.  We have a parse tree, it's just not for the snapshot
                    // we're being asked for.
                    AddClassifiedSpansForPreviousTree(
                        delegationService, classificationService, span, lastSnapshot, lastDocument, classifiedSpans);
                }
            }
        private Task ProduceTagsAsync <TClassificationService>(
            TaggerContext <IClassificationTag> context,
            DocumentSnapshotSpan spanToTag,
            IClassificationDelegationService <TClassificationService> delegationService)
            where TClassificationService : class, ILanguageService
        {
            var document = spanToTag.Document;

            // Attempt to get a classification service which will actually produce the results.
            // If we can't (because we have no Document, or because the language doesn't support
            // this service), then bail out immediately.
            var classificationService = document?.GetLanguageService <TClassificationService>();

            if (classificationService == null)
            {
                return(SpecializedTasks.EmptyTask);
            }

            return(SemanticClassificationUtilities.ProduceTagsAsync(
                       context, spanToTag, delegationService, classificationService, _typeMap));
        }
            private void AddClassifiedSpansForCurrentTree <TClassificationService>(
                IClassificationDelegationService <TClassificationService> delegationService,
                TClassificationService classificationService,
                SnapshotSpan span,
                Document document,
                List <ClassifiedSpan> classifiedSpans)
            {
                if (!_lastLineCache.TryUseCache(span, out var tempList))
                {
                    tempList = ClassificationUtilities.GetOrCreateClassifiedSpanList();

                    delegationService.AddSyntacticClassificationsAsync(
                        classificationService, document, span.Span.ToTextSpan(), tempList, CancellationToken.None).Wait(CancellationToken.None);

                    _lastLineCache.Update(span, tempList);
                }

                // simple case.  They're asking for the classifications for a tree that we already have.
                // Just get the results from the tree and return them.

                classifiedSpans.AddRange(tempList);
            }
            private IEnumerable <ITagSpan <IClassificationTag> > GetTags <TClassificationService>(
                NormalizedSnapshotSpanCollection spans,
                HostLanguageServices languageServices,
                IClassificationDelegationService <TClassificationService> delegationService) where TClassificationService : class, ILanguageService
            {
                var classificationService = languageServices.GetService <TClassificationService>();

                if (classificationService == null)
                {
                    return(null);
                }

                var classifiedSpans = ClassificationUtilities.GetOrCreateClassifiedSpanList();

                foreach (var span in spans)
                {
                    AddClassifiedSpans(delegationService, classificationService, span, classifiedSpans);
                }

                return(ClassificationUtilities.ConvertAndReturnList(
                           _typeMap, spans[0].Snapshot, classifiedSpans));
            }
        private static async Task ClassifySpansAsync <TClassificationService>(
            TaggerContext <IClassificationTag> context,
            DocumentSnapshotSpan spanToTag,
            IClassificationDelegationService <TClassificationService> delegationService,
            TClassificationService classificationService,
            ClassificationTypeMap typeMap)
        {
            try
            {
                var document     = spanToTag.Document;
                var snapshotSpan = spanToTag.SnapshotSpan;
                var snapshot     = snapshotSpan.Snapshot;

                var cancellationToken = context.CancellationToken;
                using (Logger.LogBlock(FunctionId.Tagger_SemanticClassification_TagProducer_ProduceTags, cancellationToken))
                {
                    var classifiedSpans = ClassificationUtilities.GetOrCreateClassifiedSpanList();

                    await delegationService.AddSemanticClassificationsAsync(
                        classificationService, document, snapshotSpan.Span.ToTextSpan(), classifiedSpans, cancellationToken : cancellationToken).ConfigureAwait(false);

                    ClassificationUtilities.Convert(typeMap, snapshotSpan.Snapshot, classifiedSpans, context.AddTag);
                    ClassificationUtilities.ReturnClassifiedSpanList(classifiedSpans);

                    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(SpecializedCollections.SingletonEnumerable(spanToTag));
                    context.State = version;
                }
            }
            catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
            private void AddClassifiedSpansForPreviousTree <TClassificationService>(
                IClassificationDelegationService <TClassificationService> delegationService,
                TClassificationService classificationService,
                SnapshotSpan span,
                ITextSnapshot lastSnapshot,
                Document lastDocument,
                List <ClassifiedSpan> classifiedSpans)
            {
                // Slightly more complicated case.  They're asking for the classifications for a
                // different snapshot than what we have a parse tree for.  So we first translate the span
                // that they're asking for so that is maps onto the tree that we have spans for.  We then
                // get the classifications from that tree.  We then take the results and translate them
                // back to the snapshot they want.  Finally, as some of the classifications may have
                // changed, we check for some common cases and touch them up manually so that things
                // look right for the user.

                // Note the handling of SpanTrackingModes here: We do EdgeExclusive while mapping back ,
                // and EdgeInclusive mapping forward. What I've convinced myself is that EdgeExclusive
                // is best when mapping back over a deletion, so that we don't end up classifying all of
                // the deleted code.  In most addition/modification cases, there will be overlap with
                // existing spans, and so we'll end up classifying well.  In the worst case, there is a
                // large addition that doesn't exist when we map back, and so we don't have any
                // classifications for it. That's probably okay, because:

                // 1. If it's that large, it's likely that in reality there are multiple classification
                // spans within it.

                // 2.We'll eventually call ClassificationsChanged and re-classify that region anyway.

                // When mapping back forward, we use EdgeInclusive so that in the common typing cases we
                // don't end up with half a token colored differently than the other half.

                // See bugs like http://vstfdevdiv:8080/web/wi.aspx?id=6176 for an example of what can
                // happen when this goes wrong.

                // 1) translate the requested span onto the right span for the snapshot that corresponds
                //    to the syntax tree.
                var translatedSpan = span.TranslateTo(lastSnapshot, SpanTrackingMode.EdgeExclusive);

                if (translatedSpan.IsEmpty)
                {
                    // well, there is no information we can get from previous tree, use lexer to
                    // classify given span. soon we will re-classify the region.
                    AddClassifiedSpansForTokens(delegationService, classificationService, span, classifiedSpans);
                    return;
                }

                var tempList = ClassificationUtilities.GetOrCreateClassifiedSpanList();

                AddClassifiedSpansForCurrentTree(
                    delegationService, classificationService, translatedSpan, lastDocument, tempList);

                var currentSnapshot = span.Snapshot;
                var currentText     = currentSnapshot.AsText();

                foreach (var lastClassifiedSpan in tempList)
                {
                    // 2) Translate those classifications forward so that they correspond to the true
                    //    requested snapshot.
                    var lastSnapshotSpan    = lastClassifiedSpan.TextSpan.ToSnapshotSpan(lastSnapshot);
                    var currentSnapshotSpan = lastSnapshotSpan.TranslateTo(currentSnapshot, SpanTrackingMode.EdgeInclusive);

                    var currentClassifiedSpan = new ClassifiedSpan(lastClassifiedSpan.ClassificationType, currentSnapshotSpan.Span.ToTextSpan());

                    // 3) The classifications may be incorrect due to changes in the text.  For example,
                    //    if "clss" becomes "class", then we want to changes the classification from
                    //    'identifier' to 'keyword'.
                    currentClassifiedSpan = delegationService.AdjustStaleClassification(
                        classificationService, currentText, currentClassifiedSpan);

                    classifiedSpans.Add(currentClassifiedSpan);
                }

                ClassificationUtilities.ReturnClassifiedSpanList(tempList);
            }
        private static async Task <bool> TryClassifyContainingMemberSpan <TClassificationService>(
            TaggerContext <IClassificationTag> context,
            DocumentSnapshotSpan spanToTag,
            IClassificationDelegationService <TClassificationService> delegationService,
            TClassificationService classificationService,
            ClassificationTypeMap typeMap)
        {
            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
            var document = spanToTag.Document;

            if (!document.SupportsSyntaxTree)
            {
                return(false);
            }

            var cancellationToken = context.CancellationToken;

            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.GetLanguageService <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.GetSyntaxRootAsync(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 subSpan = subTextSpan.Contains(changedSpan) ? subTextSpan.ToSpan() : member.FullSpan.ToSpan();

            var subSpanToTag = new DocumentSnapshotSpan(spanToTag.Document,
                                                        new SnapshotSpan(spanToTag.SnapshotSpan.Snapshot, subSpan));

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

            return(true);
        }