File: Peek\PeekHelpers.cs
Web Access
Project: src\src\EditorFeatures\Core.Wpf\Microsoft.CodeAnalysis.EditorFeatures.Wpf_tpal30ww_wpftmp.csproj (Microsoft.CodeAnalysis.EditorFeatures.Wpf)
// 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.
 
#nullable disable
 
using System.IO;
using System.Threading;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Language.Intellisense;
 
namespace Microsoft.CodeAnalysis.Editor.Implementation.Peek
{
    internal static class PeekHelpers
    {
        internal static IDocumentPeekResult CreateDocumentPeekResult(string filePath, LinePositionSpan identifierLocation, LinePositionSpan entityOfInterestSpan, IPeekResultFactory peekResultFactory)
        {
            var fileName = Path.GetFileName(filePath);
            var label = string.Format("{0} - ({1}, {2})", fileName, identifierLocation.Start.Line + 1, identifierLocation.Start.Character + 1);
 
            var displayInfo = new PeekResultDisplayInfo(label: label, labelTooltip: filePath, title: fileName, titleTooltip: filePath);
 
            return CreateDocumentPeekResult(
                filePath,
                identifierLocation,
                entityOfInterestSpan,
                displayInfo,
                peekResultFactory,
                isReadOnly: false);
        }
 
        internal static IDocumentPeekResult CreateDocumentPeekResult(string filePath, LinePositionSpan identifierLocation, LinePositionSpan entityOfInterestSpan, PeekResultDisplayInfo displayInfo, IPeekResultFactory peekResultFactory, bool isReadOnly)
        {
            return peekResultFactory.Create(
                displayInfo,
                filePath: filePath,
                startLine: entityOfInterestSpan.Start.Line,
                startIndex: entityOfInterestSpan.Start.Character,
                endLine: entityOfInterestSpan.End.Line,
                endIndex: entityOfInterestSpan.End.Character,
                idLine: identifierLocation.Start.Line,
                idIndex: identifierLocation.Start.Character,
                isReadOnly: isReadOnly);
        }
 
        internal static LinePositionSpan GetEntityOfInterestSpan(ISymbol symbol, Workspace workspace, Location identifierLocation, CancellationToken cancellationToken)
        {
            // This is called on a background thread, but since we don't have proper asynchrony we must block
            var root = identifierLocation.SourceTree.GetRoot(cancellationToken);
            var node = root.FindToken(identifierLocation.SourceSpan.Start).Parent;
 
            var syntaxFactsService = workspace.Services.GetLanguageServices(root.Language).GetService<ISyntaxFactsService>();
 
            switch (symbol.Kind)
            {
                case SymbolKind.Event:
                case SymbolKind.Field:
                case SymbolKind.Method:
                case SymbolKind.Property:
                    node = node.FirstAncestorOrSelf<SyntaxNode, ISyntaxFactsService>((node, syntaxFactsService) => syntaxFactsService.IsMethodLevelMember(node), syntaxFactsService) ?? node;
                    break;
 
                case SymbolKind.NamedType:
                case SymbolKind.Namespace:
                    node = node.FirstAncestorOrSelf<SyntaxNode, ISyntaxFactsService>((node, syntaxFactsService) => syntaxFactsService.IsTopLevelNodeWithMembers(node), syntaxFactsService) ?? node;
                    break;
            }
 
            return identifierLocation.SourceTree.GetLocation(node.Span).GetMappedLineSpan().Span;
        }
    }
}