ReferenceHighlightingViewTaggerProvider.TagProducer.cs 6.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Editor.Shared.Tagging;
using Microsoft.CodeAnalysis.Editor.Tagging;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting
{
    internal partial class ReferenceHighlightingViewTaggerProvider
    {
        // Internal for testing purposes.
        internal class TagProducer : ITagProducer<AbstractNavigatableReferenceHighlightingTag>
        {
            public IEqualityComparer<AbstractNavigatableReferenceHighlightingTag> TagComparer
            {
                get
                {
                    return EqualityComparer<AbstractNavigatableReferenceHighlightingTag>.Default;
                }
            }

            public void Dispose()
            {
            }

            public Task<IEnumerable<ITagSpan<AbstractNavigatableReferenceHighlightingTag>>> ProduceTagsAsync(
                IEnumerable<DocumentSnapshotSpan> snapshotSpans, SnapshotPoint? caretPosition, 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 (caretPosition == null)
                {
                    return SpecializedTasks.EmptyEnumerable<ITagSpan<AbstractNavigatableReferenceHighlightingTag>>();
                }

                var position = caretPosition.Value;

                Workspace workspace;
                if (!Workspace.TryGetWorkspace(position.Snapshot.AsText().Container, out workspace))
                {
                    return SpecializedTasks.EmptyEnumerable<ITagSpan<AbstractNavigatableReferenceHighlightingTag>>();
                }

                var document = snapshotSpans.First(vt => vt.SnapshotSpan.Snapshot == position.Snapshot).Document;
59 60 61 62
                if (document == null)
                {
                    return SpecializedTasks.EmptyEnumerable<ITagSpan<AbstractNavigatableReferenceHighlightingTag>>();
                }
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

                return ProduceTagsAsync(snapshotSpans, position, workspace, document, cancellationToken);
            }

            internal async Task<IEnumerable<ITagSpan<AbstractNavigatableReferenceHighlightingTag>>> ProduceTagsAsync(
                IEnumerable<DocumentSnapshotSpan> snapshotSpans,
                SnapshotPoint position,
                Workspace workspace,
                Document document,
                CancellationToken cancellationToken)
            {
                // Don't produce tags if the feature is not enabled.
                if (!workspace.Options.GetOption(FeatureOnOffOptions.ReferenceHighlighting, document.Project.Language))
                {
                    return SpecializedCollections.EmptyEnumerable<ITagSpan<AbstractNavigatableReferenceHighlightingTag>>();
                }

                var solution = document.Project.Solution;

                using (Logger.LogBlock(FunctionId.Tagger_ReferenceHighlighting_TagProducer_ProduceTags, cancellationToken))
                {
                    var result = new List<ITagSpan<AbstractNavigatableReferenceHighlightingTag>>();

                    if (document != null)
                    {
                        var documentHighlightsService = document.Project.LanguageServices.GetService<IDocumentHighlightsService>();
                        if (documentHighlightsService != null)
                        {
                            // We only want to search inside documents that correspond to the snapshots
                            // we're looking at
                            var documentsToSearch = ImmutableHashSet.CreateRange(snapshotSpans.Select(vt => vt.Document).WhereNotNull());
                            var documentHighlightsList = await documentHighlightsService.GetDocumentHighlightsAsync(document, position, documentsToSearch, cancellationToken).ConfigureAwait(false);
                            if (documentHighlightsList != null)
                            {
                                foreach (var documentHighlights in documentHighlightsList)
                                {
                                    await AddTagSpansAsync(solution, result, documentHighlights, cancellationToken).ConfigureAwait(false);
                                }
                            }
                        }
                    }

                    return result;
                }
            }

            private async Task AddTagSpansAsync(
                Solution solution, List<ITagSpan<AbstractNavigatableReferenceHighlightingTag>> tags, DocumentHighlights documentHighlights, CancellationToken cancellationToken)
            {
                var document = documentHighlights.Document;

                var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
                var textSnapshot = text.FindCorrespondingEditorTextSnapshot();
                if (textSnapshot == null)
                {
                    // There is no longer an editor snapshot for this document, so we can't care about the
                    // results.
                    return;
                }

                foreach (var span in documentHighlights.HighlightSpans)
                {
125
                    var tag = GetTag(span);
126 127 128 129
                    tags.Add(new TagSpan<AbstractNavigatableReferenceHighlightingTag>(
                        textSnapshot.GetSpan(Span.FromBounds(span.TextSpan.Start, span.TextSpan.End)), tag));
                }
            }
130 131 132 133 134 135 136 137 138 139 140 141 142 143

            private static AbstractNavigatableReferenceHighlightingTag GetTag(HighlightSpan span)
            {
                switch (span.Kind)
                {
                    case HighlightSpanKind.WrittenReference:
                        return WrittenReferenceHighlightTag.Instance;
                    case HighlightSpanKind.Definition:
                        return DefinitionHighlightTag.Instance;
                    case HighlightSpanKind.ReadReference:
                    default:
                        return ReferenceHighlightTag.Instance;
                }
            }
144 145 146
        }
    }
}