protected override async Task ProduceTagsAsync(TaggerContext <KeywordHighlightTag> context, DocumentSnapshotSpan documentSnapshotSpan, int?caretPosition) { var cancellationToken = context.CancellationToken; var document = documentSnapshotSpan.Document; // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/763988 // It turns out a document might be associated with a project of wrong language, e.g. C# document in a Xaml project. // Even though we couldn't repro the crash above, a fix is made in one of possibly multiple code paths that could cause // us to end up in this situation. // Regardless of the effective of the fix, we want to enhance the guard against such scenario here until an audit in // workspace is completed to eliminate the root cause. if (document?.SupportsSyntaxTree != true) { return; } var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); if (!documentOptions.GetOption(FeatureOnOffOptions.KeywordHighlighting)) { return; } if (!caretPosition.HasValue) { return; } var snapshotSpan = documentSnapshotSpan.SnapshotSpan; var position = caretPosition.Value; var snapshot = snapshotSpan.Snapshot; // See if the user is just moving their caret around in an existing tag. If so, we don't // want to actually go recompute things. Note: this only works for containment. If the // user moves their caret to the end of a highlighted reference, we do want to recompute // as they may now be at the start of some other reference that should be highlighted instead. var existingTags = context.GetExistingContainingTags(new SnapshotPoint(snapshot, position)); if (!existingTags.IsEmpty()) { context.SetSpansTagged(SpecializedCollections.EmptyEnumerable <DocumentSnapshotSpan>()); return; } using (Logger.LogBlock(FunctionId.Tagger_Highlighter_TagProducer_ProduceTags, cancellationToken)) using (s_listPool.GetPooledObject(out var highlights)) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); _highlightingService.AddHighlights(root, position, highlights, cancellationToken); foreach (var span in highlights) { context.AddTag(new TagSpan <KeywordHighlightTag>(span.ToSnapshotSpan(snapshot), KeywordHighlightTag.Instance)); } } }
protected override async Task ProduceTagsAsync(TaggerContext <KeywordHighlightTag> 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.KeywordHighlighting)) { return; } if (!caretPosition.HasValue) { return; } var snapshotSpan = documentSnapshotSpan.SnapshotSpan; var position = caretPosition.Value; var snapshot = snapshotSpan.Snapshot; // See if the user is just moving their caret around in an existing tag. If so, we don't // want to actually go recompute things. Note: this only works for containment. If the // user moves their caret to the end of a highlighted reference, we do want to recompute // as they may now be at the start of some other reference that should be highlighted instead. var existingTags = context.GetExistingContainingTags(new SnapshotPoint(snapshot, position)); if (!existingTags.IsEmpty()) { context.SetSpansTagged(SpecializedCollections.EmptyEnumerable <DocumentSnapshotSpan>()); return; } using (Logger.LogBlock(FunctionId.Tagger_Highlighter_TagProducer_ProduceTags, cancellationToken)) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var spans = _highlightingService.GetHighlights(root, position, cancellationToken); foreach (var span in spans) { context.AddTag(new TagSpan <KeywordHighlightTag>(span.ToSnapshotSpan(snapshot), KeywordHighlightTag.Instance)); } } }
protected override Task ProduceTagsAsync(TaggerContext <NavigableHighlightTag> context) { // NOTE(cyrusn): Normally we'd limit ourselves to producing tags in the span we were // asked about. However, we want to produce all tags here so that the user can actually // navigate between all of them using the appropriate tag navigation commands. If we // don't generate all the tags then the user will cycle through an incorrect subset. if (context.CaretPosition == null) { return(Task.CompletedTask); } var caretPosition = context.CaretPosition.Value; if (!Workspace.TryGetWorkspace(caretPosition.Snapshot.AsText().Container, out var workspace)) { return(Task.CompletedTask); } // GetSpansToTag may have produced no actual spans to tag. Be resilient to that. var document = context.SpansToTag.FirstOrDefault(vt => vt.SnapshotSpan.Snapshot == caretPosition.Snapshot).Document; if (document == null) { return(Task.CompletedTask); } // Don't produce tags if the feature is not enabled. if (!workspace.Options.GetOption(FeatureOnOffOptions.ReferenceHighlighting, document.Project.Language)) { return(Task.CompletedTask); } // See if the user is just moving their caret around in an existing tag. If so, we don't // want to actually go recompute things. Note: this only works for containment. If the // user moves their caret to the end of a highlighted reference, we do want to recompute // as they may now be at the start of some other reference that should be highlighted instead. var existingTags = context.GetExistingContainingTags(caretPosition); if (!existingTags.IsEmpty()) { context.SetSpansTagged(SpecializedCollections.EmptyEnumerable <DocumentSnapshotSpan>()); return(Task.CompletedTask); } // Otherwise, we need to go produce all tags. return(ProduceTagsAsync(context, caretPosition, document)); }