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);
        }
Exemple #2
0
        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));
        }