/// <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; } }
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; } }
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); }
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)); }
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; } }
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); } }
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; } }
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; } }