private async Task <ImmutableArray <DocumentHighlight> > GetReferenceHighlightsAsync(Document document, SourceText text, int position, CancellationToken cancellationToken) { var documentHighlightService = document.GetRequiredLanguageService <IDocumentHighlightsService>(); var options = _globalOptions.GetHighlightingOptions(document.Project.Language); var highlights = await documentHighlightService.GetDocumentHighlightsAsync( document, position, ImmutableHashSet.Create(document), options, cancellationToken).ConfigureAwait(false); if (!highlights.IsDefaultOrEmpty) { // LSP requests are only for a single document. So just get the highlights for the requested document. var highlightsForDocument = highlights.FirstOrDefault(h => h.Document.Id == document.Id); return(highlightsForDocument.HighlightSpans.SelectAsArray(h => new DocumentHighlight { Range = ProtocolConversions.TextSpanToRange(h.TextSpan, text), Kind = ProtocolConversions.HighlightSpanKindToDocumentHighlightKind(h.Kind), })); } return(ImmutableArray <DocumentHighlight> .Empty); }
protected override Task ProduceTagsAsync( TaggerContext <NavigableHighlightTag> context, CancellationToken cancellationToken) { // 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; // 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 (!_globalOptions.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(ImmutableArray <SnapshotSpan> .Empty); return(Task.CompletedTask); } // Otherwise, we need to go produce all tags. var options = _globalOptions.GetHighlightingOptions(document.Project.Language); return(ProduceTagsAsync(context, caretPosition, document, options, cancellationToken)); }