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