/// <summary>
        /// Keep this in sync with <see cref="ProduceTagsAsync"/>
        /// </summary>
        protected sealed override void ProduceTagsSynchronously(
            TaggerContext <IStructureTag> context, DocumentSnapshotSpan documentSnapshotSpan, int?caretPosition)
        {
            try
            {
                var document = documentSnapshotSpan.Document;
                if (document == null)
                {
                    return;
                }

                // Let LSP handle producing tags in the cloud scenario
                if (documentSnapshotSpan.SnapshotSpan.Snapshot.TextBuffer.IsInLspEditorContext())
                {
                    return;
                }

                var outliningService = BlockStructureService.GetService(document);
                if (outliningService == null)
                {
                    return;
                }

                var blockStructure = outliningService.GetBlockStructure(document, context.CancellationToken);
                ProcessSpans(
                    context, documentSnapshotSpan.SnapshotSpan, outliningService,
                    blockStructure.Spans);
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
Esempio n. 2
0
        protected sealed override void ProduceTagsSynchronously(
            TaggerContext <TRegionTag> context, DocumentSnapshotSpan documentSnapshotSpan, int?caretPosition)
        {
            try
            {
                // Let LSP handle producing tags in the cloud scenario
                if (documentSnapshotSpan.Document.IsInCloudEnvironmentClientContext())
                {
                    return;
                }

                var outliningService = AbstractStructureTaggerProvider <TRegionTag> .TryGetService(context, documentSnapshotSpan);

                if (outliningService != null)
                {
                    var document          = documentSnapshotSpan.Document;
                    var cancellationToken = context.CancellationToken;

                    // Try to call through the synchronous service if possible. Otherwise, fallback
                    // and make a blocking call against the async service.

                    var blockStructure = outliningService.GetBlockStructure(document, cancellationToken);

                    ProcessSpans(
                        context, documentSnapshotSpan.SnapshotSpan, outliningService,
                        blockStructure.Spans);
                }
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
Esempio n. 3
0
        protected override async Task ProduceTagsAsync(
            TaggerContext <LineSeparatorTag> context,
            DocumentSnapshotSpan documentSnapshotSpan,
            int?caretPosition
            )
        {
            var cancellationToken = context.CancellationToken;
            var document          = documentSnapshotSpan.Document;

            if (document == null)
            {
                return;
            }

            var documentOptions = await document
                                  .GetOptionsAsync(cancellationToken)
                                  .ConfigureAwait(false);

            if (!documentOptions.GetOption(FeatureOnOffOptions.LineSeparator))
            {
                return;
            }

            LineSeparatorTag tag;

            lock (_lineSeperatorTagGate)
            {
                tag = _lineSeparatorTag;
            }

            using (
                Logger.LogBlock(
                    FunctionId.Tagger_LineSeparator_TagProducer_ProduceTags,
                    cancellationToken
                    )
                )
            {
                var snapshotSpan         = documentSnapshotSpan.SnapshotSpan;
                var lineSeparatorService = document.GetLanguageService <ILineSeparatorService>();
                var lineSeparatorSpans   = await lineSeparatorService
                                           .GetLineSeparatorsAsync(
                    document,
                    snapshotSpan.Span.ToTextSpan(),
                    cancellationToken
                    )
                                           .ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested();

                foreach (var span in lineSeparatorSpans)
                {
                    context.AddTag(
                        new TagSpan <LineSeparatorTag>(
                            span.ToSnapshotSpan(snapshotSpan.Snapshot),
                            tag
                            )
                        );
                }
            }
        }
        protected override async Task ProduceTagsAsync(
            TaggerContext <IClassificationTag> context,
            DocumentSnapshotSpan snapshotSpan,
            int?caretPosition)
        {
            // Asked to classify when the document is no longer part of a workspace,
            // this can happen when the document/project is being closed.
            if (snapshotSpan.Document == null)
            {
                return;
            }

            // Classify documents in chunks of 50k.   This allows more important work (like
            // completion) to pre-empt classification on our semantic thread.  It also keeps
            // the size of the responses that we need to marshal from the script side smaller.

            var       document  = snapshotSpan.Document;
            var       snapshot  = snapshotSpan.SnapshotSpan.Snapshot;
            var       start     = snapshotSpan.SnapshotSpan.Start.Position;
            var       end       = snapshotSpan.SnapshotSpan.End.Position;
            const int chunkSize = 50 * 1024;

            for (var i = start; i < end; i += chunkSize)
            {
                var subSpan = Span.FromBounds(i, Math.Min(i + chunkSize, end));
                await this.ProduceTagsAsync(
                    context,
                    new DocumentSnapshotSpan(document, new SnapshotSpan(snapshot, subSpan))).ConfigureAwait(false);
            }
        }
        private static async Task<bool> TryClassifyContainingMemberSpan(TaggerContext<IClassificationTag> context, DocumentSnapshotSpan spanToTag,
            IEditorClassificationService 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;
            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.Project.LanguageServices.GetService<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, classificationService, typeMap).ConfigureAwait(false);
            return true;
        }
            private static Task ProduceTagsAsync(
                TaggerContext <IClassificationTag> context, DocumentSnapshotSpan documentSpan, ClassificationTypeMap typeMap, CancellationToken cancellationToken)
            {
                var classificationService = documentSpan.Document.GetLanguageService <IClassificationService>();

                return(classificationService != null
                    ? SemanticClassificationUtilities.ProduceTagsAsync(context, documentSpan, classificationService, typeMap, cancellationToken)
                    : Task.CompletedTask);
            }
        private void ProduceTags(
            TaggerContext <TTag> context, DocumentSnapshotSpan spanToTag,
            Workspace workspace, Document document, SourceText sourceText,
            NormalizedSnapshotSpanCollection suppressedDiagnosticsSpans,
            UpdatedEventArgs updateArgs, CancellationToken cancellationToken)
        {
            try
            {
                var id          = updateArgs.Id;
                var diagnostics = _diagnosticService.GetDiagnostics(
                    workspace, document.Project.Id, document.Id, id, false, cancellationToken);

                var isLiveUpdate = id is ISupportLiveUpdate;

                var requestedSpan  = spanToTag.SnapshotSpan;
                var editorSnapshot = requestedSpan.Snapshot;

                foreach (var diagnosticData in diagnostics)
                {
                    if (this.IncludeDiagnostic(diagnosticData))
                    {
                        // We're going to be retrieving the diagnostics against the last time the engine
                        // computed them against this document *id*.  That might have been a different
                        // version of the document vs what we're looking at now.  But that's ok:
                        //
                        // 1) GetExistingOrCalculatedTextSpan will ensure that the diagnostics spans are
                        //    contained within 'editorSnapshot'.
                        // 2) We'll eventually hear about an update to the diagnostics for this document
                        //    for whatever edits happened between the last time and this current snapshot.
                        //    So we'll eventually reach a point where the diagnostics exactly match the
                        //    editorSnapshot.

                        var diagnosticSpan = diagnosticData.GetExistingOrCalculatedTextSpan(sourceText)
                                             .ToSnapshotSpan(editorSnapshot);

                        if (diagnosticSpan.IntersectsWith(requestedSpan) &&
                            !IsSuppressed(suppressedDiagnosticsSpans, diagnosticSpan))
                        {
                            var tagSpan = this.CreateTagSpan(isLiveUpdate, diagnosticSpan, diagnosticData);
                            if (tagSpan != null)
                            {
                                context.AddTag(tagSpan);
                            }
                        }
                    }
                }
            }
            catch (ArgumentOutOfRangeException ex) when(FatalError.ReportWithoutCrash(ex))
            {
                // https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=428328&_a=edit&triage=false
                // explicitly report NFW to find out what is causing us for out of range.
                // stop crashing on such occations
                return;
            }
        }
        public static async Task ProduceTagsAsync(
            TaggerContext <IClassificationTag> context,
            DocumentSnapshotSpan spanToTag,
            IClassificationService classificationService,
            ClassificationTypeMap typeMap
            )
        {
            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(context.CancellationToken);
            spanToTag = new DocumentSnapshotSpan(document, spanToTag.SnapshotSpan);

            var classified = await TryClassifyContainingMemberSpanAsync(
                context,
                spanToTag,
                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, classificationService, typeMap)
            .ConfigureAwait(false);
        }
        public static async Task ProduceTagsAsync(TaggerContext<IClassificationTag> context, DocumentSnapshotSpan spanToTag,
            IEditorClassificationService classificationService, ClassificationTypeMap typeMap)
        {
            var document = spanToTag.Document;
            if (document == null)
            {
                return;
            }

            if (await TryClassifyContainingMemberSpan(context, spanToTag, classificationService, typeMap).ConfigureAwait(false))
            {
                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, classificationService, typeMap).ConfigureAwait(false);
        }
Esempio n. 10
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);
            }
            public IEnumerable <ITagSpan <IClassificationTag> > GetAllTags(NormalizedSnapshotSpanCollection spans, CancellationToken cancellationToken)
            {
                this.AssertIsForeground();
                if (spans.Count == 0)
                {
                    return(Array.Empty <ITagSpan <IClassificationTag> >());
                }

                var firstSpan = spans.First();
                var snapshot  = firstSpan.Snapshot;

                Debug.Assert(snapshot.TextBuffer == _subjectBuffer);

                var cachedSnapshot = this.CachedSnapshot;

                if (snapshot != cachedSnapshot)
                {
                    // Our cache is not there, or is out of date.  We need to compute the up to date
                    // results.

                    var document = snapshot.GetOpenDocumentInCurrentContextWithChanges();
                    if (document == null)
                    {
                        return(Array.Empty <ITagSpan <IClassificationTag> >());
                    }

                    _classificationService = _classificationService ?? document.Project.LanguageServices.GetService <IEditorClassificationService>();

                    var context   = new TaggerContext <IClassificationTag>(document, snapshot, cancellationToken: cancellationToken);
                    var spanToTag = new DocumentSnapshotSpan(document, snapshot.GetFullSpan());
                    var task      = SemanticClassificationUtilities.ProduceTagsAsync(context, spanToTag, _classificationService, _owner._typeMap);
                    task.Wait(cancellationToken);

                    CachedSnapshot = snapshot;
                    CachedTags     = new TagSpanIntervalTree <IClassificationTag>(snapshot.TextBuffer, SpanTrackingMode.EdgeExclusive, context.tagSpans);
                }

                if (this.CachedTags == null)
                {
                    return(Array.Empty <ITagSpan <IClassificationTag> >());
                }

                return(this.CachedTags.GetIntersectingTagSpans(spans));
            }
Esempio n. 12
0
        private static async Task ClassifySpansAsync(
            TaggerContext <IClassificationTag> context,
            DocumentSnapshotSpan spanToTag,
            IClassificationService classificationService,
            ClassificationTypeMap typeMap,
            ClassificationOptions options,
            CancellationToken cancellationToken)
        {
            try
            {
                var document     = spanToTag.Document;
                var snapshotSpan = spanToTag.SnapshotSpan;
                var snapshot     = snapshotSpan.Snapshot;

                using (Logger.LogBlock(FunctionId.Tagger_SemanticClassification_TagProducer_ProduceTags, cancellationToken))
                {
                    using var _ = ArrayBuilder <ClassifiedSpan> .GetInstance(out var classifiedSpans);

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

                    foreach (var span in classifiedSpans)
                    {
                        context.AddTag(ClassificationUtilities.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(SpecializedCollections.SingletonEnumerable(spanToTag));
                    context.State = version;
                }
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
Esempio n. 13
0
        private IOutliningService TryGetOutliningService(
            TaggerContext <IOutliningRegionTag> context,
            DocumentSnapshotSpan documentSnapshotSpan)
        {
            var cancellationToken = context.CancellationToken;

            using (Logger.LogBlock(FunctionId.Tagger_Outlining_TagProducer_ProduceTags, cancellationToken))
            {
                var document     = documentSnapshotSpan.Document;
                var snapshotSpan = documentSnapshotSpan.SnapshotSpan;
                var snapshot     = snapshotSpan.Snapshot;

                if (document != null)
                {
                    return(document.Project.LanguageServices.GetService <IOutliningService>());
                }
            }

            return(null);
        }
        protected override Task ProduceTagsAsync(
            TaggerContext <BraceHighlightTag> context,
            DocumentSnapshotSpan documentSnapshotSpan,
            int?caretPosition
            )
        {
            var document = documentSnapshotSpan.Document;

            if (!caretPosition.HasValue || document == null)
            {
                return(Task.CompletedTask);
            }

            return(ProduceTagsAsync(
                       context,
                       document,
                       documentSnapshotSpan.SnapshotSpan.Snapshot,
                       caretPosition.Value
                       ));
        }
        private void ProduceTags(TaggerContext <TTag> context, DocumentSnapshotSpan spanToTag)
        {
            if (!this.IsEnabled)
            {
                return;
            }

            var document = spanToTag.Document;

            if (document == null)
            {
                return;
            }

            var editorSnapshot = spanToTag.SnapshotSpan.Snapshot;

            var cancellationToken = context.CancellationToken;
            var workspace         = document.Project.Solution.Workspace;

            // See if we've marked any spans as those we want to suppress diagnostics for.
            // This can happen for buffers used in the preview workspace where some feature
            // is generating code that it doesn't want errors shown for.
            var buffer = editorSnapshot.TextBuffer;
            var suppressedDiagnosticsSpans = default(NormalizedSnapshotSpanCollection);

            buffer?.Properties.TryGetProperty(PredefinedPreviewTaggerKeys.SuppressDiagnosticsSpansKey, out suppressedDiagnosticsSpans);

            var eventArgs = _diagnosticService.GetDiagnosticsUpdatedEventArgs(
                workspace, document.Project.Id, document.Id, context.CancellationToken);

            var sourceText = editorSnapshot.AsText();

            foreach (var updateArg in eventArgs)
            {
                ProduceTags(
                    context, spanToTag, workspace, document, sourceText,
                    suppressedDiagnosticsSpans, updateArg, cancellationToken);
            }
        }
Esempio n. 16
0
        protected sealed override async Task ProduceTagsAsync(
            TaggerContext <TRegionTag> context, DocumentSnapshotSpan documentSnapshotSpan, int?caretPosition)
        {
            try
            {
                var outliningService = AbstractStructureTaggerProvider <TRegionTag> .TryGetService(context, documentSnapshotSpan);

                if (outliningService != null)
                {
                    var blockStructure = await outliningService.GetBlockStructureAsync(
                        documentSnapshotSpan.Document, context.CancellationToken).ConfigureAwait(false);

                    ProcessSpans(
                        context, documentSnapshotSpan.SnapshotSpan, outliningService,
                        blockStructure.Spans);
                }
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
Esempio n. 17
0
            private static Task ProduceTagsAsync(
                TaggerContext <IClassificationTag> context,
                DocumentSnapshotSpan documentSpan,
                ClassificationTypeMap typeMap
                )
            {
                var document = documentSpan.Document;

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

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

                return(Task.CompletedTask);
            }
        private static async Task ClassifySpansAsync(TaggerContext<IClassificationTag> context, DocumentSnapshotSpan spanToTag,
            IEditorClassificationService 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 classificationService.AddSemanticClassificationsAsync(
                        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;
            }
        }