diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 000857eca1df7d24f2704ee30a685387d1cfadc1..9edc6adfec5091953852f006b4a6a11ed3a7c6eb 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -165,6 +165,7 @@ + diff --git a/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IDocumentHighlightsService.cs b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IDocumentHighlightsService.cs new file mode 100644 index 0000000000000000000000000000000000000000..c36aaed9954cd77c0a28ea5c7d01da5222f154ba --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/ReferenceHighlighting/IDocumentHighlightsService.cs @@ -0,0 +1,53 @@ +// 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.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Editor +{ + internal enum HighlightSpanKind + { + None, + Definition, + Reference, + WrittenReference, + } + + internal struct HighlightSpan + { + public TextSpan TextSpan { get; } + public HighlightSpanKind Kind { get; } + + public HighlightSpan(TextSpan textSpan, HighlightSpanKind kind) : this() + { + this.TextSpan = textSpan; + this.Kind = kind; + } + } + + internal struct DocumentHighlights + { + public Document Document { get; } + public ImmutableArray HighlightSpans { get; } + + public DocumentHighlights(Document document, ImmutableArray highlightSpans) + { + this.Document = document; + this.HighlightSpans = highlightSpans; + } + } + + /// + /// Note: kept around for back compat until F# and TypeScript move over to + /// . + /// + internal interface IDocumentHighlightsService : ILanguageService + { + Task> GetDocumentHighlightsAsync( + Document document, int position, IImmutableSet documentsToSearch, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs b/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs index e2367ad5f5d3de2b313a1046520578c9566c928d..94ab3331cb6680944eee3fb902d8fc5f34e7dd42 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs @@ -5,14 +5,17 @@ using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Tagging; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; @@ -111,13 +114,12 @@ protected override Task ProduceTagsAsync(TaggerContext co } // Otherwise, we need to go produce all tags. - return ProduceTagsAsync(context, caretPosition, workspace, document); + return ProduceTagsAsync(context, caretPosition, document); } internal async Task ProduceTagsAsync( TaggerContext context, SnapshotPoint position, - Workspace workspace, Document document) { var cancellationToken = context.CancellationToken; @@ -128,25 +130,73 @@ protected override Task ProduceTagsAsync(TaggerContext co { if (document != null) { - var documentHighlightsService = document.Project.LanguageServices.GetService(); - if (documentHighlightsService != null) + // As we transition to the new API (defined at the Features layer) we support + // calling into both it and the old API (defined at the EditorFeatures layer). + // + // Once TypeScript and F# can move over, then we can remove the calls to the old + // API. + await TryNewServiceAsync(context, position, document).ConfigureAwait(false); + await TryOldServiceAsync(context, position, document).ConfigureAwait(false); + } + } + } + + private Task TryOldServiceAsync(TaggerContext context, SnapshotPoint position, Document document) + { + return TryServiceAsync( + context, position, document, + (s, d, p, ds, c) => s.GetDocumentHighlightsAsync(d, p, ds, c)); + } + + private Task TryNewServiceAsync( + TaggerContext context, SnapshotPoint position, Document document) + { + return TryServiceAsync( + context, position, document, + async (service, doc, point, documents, cancellation) => + { + // Call into the new service. + var newHighlights = await service.GetDocumentHighlightsAsync(doc, point, documents, cancellation).ConfigureAwait(false); + + // then convert the result to the form the old service would return. + return ConvertHighlights(newHighlights); + }); + } + + private async Task TryServiceAsync( + TaggerContext context, SnapshotPoint position, Document document, + Func, CancellationToken, Task>> getDocumentHighlightsAsync) + where T : class, ILanguageService + { + var cancellationToken = context.CancellationToken; + var documentHighlightsService = document.GetLanguageService(); + if (documentHighlightsService != null) + { + // We only want to search inside documents that correspond to the snapshots + // we're looking at + var documentsToSearch = ImmutableHashSet.CreateRange(context.SpansToTag.Select(vt => vt.Document).WhereNotNull()); + var documentHighlightsList = await getDocumentHighlightsAsync( + documentHighlightsService, document, position, documentsToSearch, cancellationToken).ConfigureAwait(false); + if (documentHighlightsList != null) + { + foreach (var documentHighlights in documentHighlightsList) { - // We only want to search inside documents that correspond to the snapshots - // we're looking at - var documentsToSearch = ImmutableHashSet.CreateRange(context.SpansToTag.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(context, solution, documentHighlights).ConfigureAwait(false); - } - } + await AddTagSpansAsync( + context, document.Project.Solution, documentHighlights).ConfigureAwait(false); } } } } + private ImmutableArray ConvertHighlights(ImmutableArray newHighlights) + => newHighlights.SelectAsArray( + documentHighlights => new DocumentHighlights( + documentHighlights.Document, + documentHighlights.HighlightSpans.SelectAsArray( + highlightSpan => new HighlightSpan( + highlightSpan.TextSpan, + (HighlightSpanKind)highlightSpan.Kind)))); + private async Task AddTagSpansAsync( TaggerContext context, Solution solution,