File: Library\VsNavInfo\NavInfo.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_pxr0p0dn_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// 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;
using System.Collections.Immutable;
using Microsoft.VisualStudio.Shell.Interop;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.VsNavInfo;
 
internal class NavInfo : IVsNavInfo, IVsNavInfo2
{
    private readonly NavInfoFactory _factory;
    private readonly string _libraryName;
    private readonly string _referenceOwnerName;
    private readonly string _namespaceName;
    private readonly string _className;
    private readonly string _memberName;
 
    private readonly _LIB_LISTTYPE _symbolType;
 
    private readonly ImmutableArray<NavInfoNode> _baseCanonicalNodes;
    private readonly ImmutableArray<NavInfoNode> _basePresentationNodes;
 
    private ImmutableArray<NavInfoNode> _canonicalEnumNodes;
    private ImmutableArray<NavInfoNode> _objectBrowserEnumNodes;
    private ImmutableArray<NavInfoNode> _classViewEnumNodes;
 
    /// <summary>
    /// Creates a new NavInfo object that implements <see cref="IVsNavInfo"/> and <see cref="IVsNavInfo2"/>.
    /// </summary>
    /// <param name="factory">The <see cref="NavInfoFactory"/> that created this NavInfo.</param>
    /// <param name="libraryName">The name of the library (project or assembly) to use for navigation.</param>
    /// <param name="referenceOwnerName">If this NavInfo is inside of an assembly or project reference, this is the name of the project
    /// that owns the reference. In general, this is only set when the NavInfo is constructed from the Class View window, where references
    /// are parented inside of projects.</param>
    /// <param name="namespaceName">The name of the namespace used for navigation.</param>
    /// <param name="className">The name of the class used for navigation (should be contained by <paramref name="namespaceName"/>).</param>
    /// <param name="memberName">The name of the member used for navigation (should be contained by <paramref name="memberName"/>).</param>
    public NavInfo(
        NavInfoFactory factory,
        string libraryName,
        string referenceOwnerName = null,
        string namespaceName = null,
        string className = null,
        string memberName = null)
    {
        _factory = factory;
 
        _libraryName = libraryName;
        _referenceOwnerName = referenceOwnerName;
        _namespaceName = namespaceName;
        _className = className;
        _memberName = memberName;
 
        _baseCanonicalNodes = CreateNodes(expandDottedNames: true);
        _basePresentationNodes = CreateNodes(expandDottedNames: false);
 
        _symbolType = _basePresentationNodes.Length > 0
            ? _basePresentationNodes[^1].ListType
            : 0;
    }
 
    private ImmutableArray<NavInfoNode> CreateNodes(bool expandDottedNames)
    {
        var builder = ImmutableArray.CreateBuilder<NavInfoNode>();
 
        // Note: we only expand dotted names for namespaces and classes.
 
        if (_referenceOwnerName != null)
        {
            builder.Add(_referenceOwnerName, _LIB_LISTTYPE.LLT_PACKAGE);
            builder.Add(ServicesVSResources.Project_References, _LIB_LISTTYPE.LLT_HIERARCHY);
        }
 
        builder.Add(_libraryName, _LIB_LISTTYPE.LLT_PACKAGE);
        builder.Add(_namespaceName, _LIB_LISTTYPE.LLT_NAMESPACES, expandDottedNames);
        builder.Add(_className, _LIB_LISTTYPE.LLT_CLASSES, expandDottedNames);
        builder.Add(_memberName, _LIB_LISTTYPE.LLT_MEMBERS);
 
        return builder.ToImmutable();
    }
 
    private static NavInfoNodeEnum CreateEnum(ref ImmutableArray<NavInfoNode> nodes, ImmutableArray<NavInfoNode> baseNodes, bool isCanonical, bool isObjectBrowser)
    {
        if (nodes.IsDefault)
        {
            var builder = ImmutableArray.CreateBuilder<NavInfoNode>();
 
            var startIndex = 0;
 
            // In some cases, Class View presentation NavInfo objects will have extra nodes (LLT_PACKAGE & LLT_HIERARCHY) up front.
            // When this NavInfo is consumed by Object Browser (for 'Browse to Definition'), we need to skip first two nodes
            if (isObjectBrowser && !isCanonical && baseNodes is [_, { ListType: _LIB_LISTTYPE.LLT_HIERARCHY }, ..])
            {
                startIndex = 2;
            }
 
            for (var i = startIndex; i < baseNodes.Length; i++)
            {
                if (isCanonical && baseNodes[i].ListType == _LIB_LISTTYPE.LLT_HIERARCHY)
                {
                    continue;
                }
 
                builder.Add(baseNodes[i]);
            }
 
            nodes = builder.ToImmutable();
        }
 
        return new NavInfoNodeEnum(nodes);
    }
 
    public int EnumCanonicalNodes(out IVsEnumNavInfoNodes ppEnum)
    {
        ppEnum = CreateEnum(ref _canonicalEnumNodes, _baseCanonicalNodes, isCanonical: true, isObjectBrowser: false);
        return VSConstants.S_OK;
    }
 
    public int EnumPresentationNodes(uint dwFlags, out IVsEnumNavInfoNodes ppEnum)
    {
        ppEnum = dwFlags == (uint)_LIB_LISTFLAGS.LLF_NONE
            ? CreateEnum(ref _objectBrowserEnumNodes, _basePresentationNodes, isCanonical: false, isObjectBrowser: true)
            : CreateEnum(ref _classViewEnumNodes, _basePresentationNodes, isCanonical: false, isObjectBrowser: false);
 
        return VSConstants.S_OK;
    }
 
    public int GetLibGuid(out Guid pGuid)
    {
        pGuid = _factory.LibraryService.LibraryId;
        return VSConstants.S_OK;
    }
 
    public void GetPreferredLanguage(out uint pLanguage)
        => pLanguage = (uint)_factory.LibraryService.PreferredLanguage;
 
    public int GetSymbolType(out uint pdwType)
    {
        pdwType = (uint)_symbolType;
        return VSConstants.S_OK;
    }
}