|
// 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;
}
}
|