| File: Navigation\IDefinitionLocationService.cs | Web Access |
| Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Navigation; /// <summary> /// Service used by "go to definition" and "ctrl-click on symbol" to find the symbol definition location and navigate to /// it. Specifically, services that do not intend to show any interesting UI for the symbol definition, they just intend /// to navigate to it. If richer information is desired (like determining what to display for the symbol name), then /// <see cref="INavigableItemsService"/> should be used instead. /// </summary> internal interface IDefinitionLocationService : ILanguageService { /// <summary> /// If the supplied <paramref name="position"/> is on a code construct with a navigable location, then this /// returns that <see cref="INavigableLocation"/>. The <see cref="TextSpan"/> returned in the span of the /// symbol in the code that references that navigable location. e.g. the full identifier token that the /// position is within. /// </summary> Task<DefinitionLocation?> GetDefinitionLocationAsync( Document document, int position, CancellationToken cancellationToken); } /// <summary> /// The result of a <see cref="IDefinitionLocationService.GetDefinitionLocationAsync"/> call. /// </summary> /// <param name="Location">The location where the symbol is actually defined at. Can be used to then navigate to that /// symbol. /// </param> /// <param name="Span">The <see cref="TextSpan"/> returned in the span of the symbol in the code that references that /// navigable location. e.g. the full identifier token that the position is within. Can be used to highlight/underline /// that text in the document in some fashion.</param> internal sealed record DefinitionLocation(INavigableLocation Location, DocumentSpan Span); internal static class DefinitionLocationServiceHelpers { public static async Task<DefinitionLocation?> GetDefinitionLocationFromLegacyImplementationsAsync( Document document, int position, Func<CancellationToken, Task<IEnumerable<(Document document, TextSpan sourceSpan)>?>> getNavigableItems, CancellationToken cancellationToken) { var items = await getNavigableItems(cancellationToken).ConfigureAwait(false); if (items is null) return null; var firstItem = items.FirstOrNull(); if (firstItem is null) return null; var navigableItem = await new DocumentSpan(firstItem.Value.document, firstItem.Value.sourceSpan).GetNavigableLocationAsync(cancellationToken).ConfigureAwait(false); if (navigableItem is null) return null; var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); return new DefinitionLocation(navigableItem, GetDocumentSpan()); DocumentSpan GetDocumentSpan() { // To determine the span to underline, just use a simple heuristic of expanding out to the surrounding // letters and numbers. F#/TS can reimplement this in the future to be more accurate to their language. var startPosition = position; var endPosition = position + 1; if (char.IsLetterOrDigit(text[position])) { while (char.IsLetterOrDigit(GetChar(startPosition - 1))) startPosition--; while (char.IsLetterOrDigit(GetChar(endPosition + 1))) endPosition++; } return new DocumentSpan(document, TextSpan.FromBounds(startPosition, endPosition)); } char GetChar(int position) => position >= 0 && position < text.Length ? text[position] : (char)0; } }