File: Library\ObjectBrowser\ObjectList.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_4volmoxl_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 System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.VisualStudio.LanguageServices.Implementation.F1Help;
using Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser.Lists;
using Microsoft.VisualStudio.LanguageServices.Implementation.Library.VsNavInfo;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser;
 
internal class ObjectList : AbstractObjectList<AbstractObjectBrowserLibraryManager>
{
    private readonly ObjectList _parentList;
    private readonly uint _flags;
    private readonly ImmutableArray<ObjectListItem> _items;
 
    public ObjectList(
        ObjectListKind kind,
        uint flags,
        AbstractObjectBrowserLibraryManager manager,
        ImmutableArray<ObjectListItem> items)
        : this(kind, flags, null, null, manager, items)
    {
    }
 
    public ObjectList(
        ObjectListKind kind,
        uint flags,
        ObjectList parentList,
        ObjectListItem parentListItem,
        AbstractObjectBrowserLibraryManager manager,
        ImmutableArray<ObjectListItem> items)
        : base(manager)
    {
        Kind = kind;
        _flags = flags;
        _parentList = parentList;
        ParentListItem = parentListItem;
 
        _items = items;
 
        foreach (var item in _items)
        {
            item.SetParentList(this);
        }
    }
 
    private bool IsClassView()
        => Helpers.IsClassView(_flags);
 
    private ObjectListItem GetListItem(uint index)
    {
        var listItem = _items[(int)index];
        this.LibraryManager.SetActiveListItem(listItem);
        return listItem;
    }
 
    private string GetDisplayText(uint index, VSTREETEXTOPTIONS textOptions)
    {
        var listItem = GetListItem(index);
 
        switch (textOptions)
        {
            case VSTREETEXTOPTIONS.TTO_SORTTEXT:
            case VSTREETEXTOPTIONS.TTO_DISPLAYTEXT:
                switch (Kind)
                {
                    case ObjectListKind.BaseTypes:
                    case ObjectListKind.Hierarchy:
                    case ObjectListKind.Members:
                    case ObjectListKind.Namespaces:
                    case ObjectListKind.Projects:
                    case ObjectListKind.References:
                    case ObjectListKind.Types:
                        return listItem.DisplayText;
                }
 
                break;
        }
 
        return listItem.DisplayText;
    }
 
    protected override bool CanGoToSource(uint index, VSOBJGOTOSRCTYPE srcType)
    {
        if (srcType == VSOBJGOTOSRCTYPE.GS_DEFINITION)
        {
            if (GetListItem(index) is SymbolListItem symbolItem)
            {
                return symbolItem.SupportsGoToDefinition;
            }
        }
 
        return false;
    }
 
    protected override bool TryGetCapabilities(out uint capabilities)
    {
        capabilities = (uint)_LIB_LISTCAPABILITIES2.LLC_ALLOWELEMENTSEARCH;
        return true;
    }
 
    private bool TryGetListType(out uint categoryField)
    {
        switch (Kind)
        {
            case ObjectListKind.BaseTypes:
                categoryField = (uint)_LIB_LISTTYPE.LLT_CLASSES | (uint)_LIB_LISTTYPE.LLT_MEMBERS;
                return true;
 
            case ObjectListKind.Hierarchy:
                var parentKind = this.ParentKind;
                categoryField = parentKind is ObjectListKind.Types or ObjectListKind.BaseTypes
                    ? (uint)_LIB_LISTTYPE.LLT_CLASSES
                    : (uint)_LIB_LISTTYPE.LLT_PACKAGE;
 
                return true;
 
            case ObjectListKind.Members:
                categoryField = 0;
                return true;
 
            case ObjectListKind.Namespaces:
                categoryField = (uint)_LIB_LISTTYPE.LLT_CLASSES;
                return true;
 
            case ObjectListKind.Projects:
                categoryField = (uint)_LIB_LISTTYPE.LLT_NAMESPACES | (uint)_LIB_LISTTYPE.LLT_CLASSES;
 
                if (IsClassView() && this.ParentKind == ObjectListKind.None)
                {
                    categoryField |= (uint)_LIB_LISTTYPE.LLT_HIERARCHY;
                }
 
                return true;
 
            case ObjectListKind.References:
                categoryField = (uint)_LIB_LISTTYPE.LLT_NAMESPACES | (uint)_LIB_LISTTYPE.LLT_CLASSES;
                return true;
 
            case ObjectListKind.Types:
                categoryField = (uint)_LIB_LISTTYPE.LLT_MEMBERS;
 
                if ((_flags & (Helpers.LLF_SEARCH_EXPAND_MEMBERS | Helpers.LLF_SEARCH_WITH_EXPANSION)) == 0)
                {
                    categoryField |= (uint)_LIB_LISTTYPE.LLT_HIERARCHY;
                }
 
                return true;
        }
 
        categoryField = 0;
        return false;
    }
 
    private bool TryGetClassAccess(uint index, out uint categoryField)
    {
        var typeListItem = (TypeListItem)GetListItem(index);
        switch (typeListItem.Accessibility)
        {
            case Accessibility.Private:
                categoryField = (uint)_LIBCAT_MEMBERACCESS.LCMA_PRIVATE;
                return true;
 
            case Accessibility.Protected:
                categoryField = (uint)_LIBCAT_MEMBERACCESS.LCMA_PROTECTED;
                return true;
 
            case Accessibility.Internal:
            case Accessibility.ProtectedOrInternal:
            case Accessibility.ProtectedAndInternal:
                categoryField = (uint)_LIBCAT_MEMBERACCESS.LCMA_PACKAGE;
                return true;
 
            default:
                categoryField = (uint)_LIBCAT_MEMBERACCESS.LCMA_PUBLIC;
                return true;
        }
    }
 
    private bool TryGetClassType(uint index, out uint categoryField)
    {
        var typeListItem = (TypeListItem)GetListItem(index);
        switch (typeListItem.Kind)
        {
            case TypeKind.Interface:
                categoryField = (uint)_LIBCAT_CLASSTYPE.LCCT_INTERFACE;
                return true;
 
            case TypeKind.Struct:
                categoryField = (uint)_LIBCAT_CLASSTYPE.LCCT_STRUCT;
                return true;
 
            case TypeKind.Enum:
                categoryField = (uint)_LIBCAT_CLASSTYPE.LCCT_ENUM;
                return true;
 
            case TypeKind.Delegate:
                categoryField = (uint)_LIBCAT_CLASSTYPE.LCCT_DELEGATE;
                return true;
 
            case TypeKind.Class:
                categoryField = (uint)_LIBCAT_CLASSTYPE.LCCT_CLASS;
                return true;
 
            case TypeKind.Module:
                categoryField = (uint)_LIBCAT_CLASSTYPE.LCCT_MODULE;
                return true;
 
            default:
                Debug.Fail("Unexpected type kind: " + typeListItem.Kind.ToString());
                categoryField = 0;
                return false;
        }
    }
 
    private bool TryGetMemberInheritance(uint index, out uint categoryField)
    {
        var memberListItem = (MemberListItem)GetListItem(index);
        if (memberListItem.IsInherited)
        {
            categoryField = (uint)_LIBCAT_MEMBERINHERITANCE.LCMI_INHERITED;
        }
        else
        {
            categoryField = (uint)_LIBCAT_MEMBERINHERITANCE.LCMI_IMMEDIATE;
        }
 
        return true;
    }
 
    private bool TryGetMemberAccess(uint index, out uint categoryField)
    {
        var memberListItem = (MemberListItem)GetListItem(index);
        switch (memberListItem.Accessibility)
        {
            case Accessibility.Private:
                categoryField = (uint)_LIBCAT_MEMBERACCESS.LCMA_PRIVATE;
                return true;
 
            case Accessibility.Protected:
                categoryField = (uint)_LIBCAT_MEMBERACCESS.LCMA_PROTECTED;
                return true;
 
            case Accessibility.Internal:
            case Accessibility.ProtectedOrInternal:
            case Accessibility.ProtectedAndInternal:
                categoryField = (uint)_LIBCAT_MEMBERACCESS.LCMA_PACKAGE;
                return true;
 
            default:
                categoryField = (uint)_LIBCAT_MEMBERACCESS.LCMA_PUBLIC;
                return true;
        }
    }
 
    private bool TryGetMemberType(uint index, out uint categoryField)
    {
        var memberListItem = (MemberListItem)GetListItem(index);
        switch (memberListItem.Kind)
        {
            case MemberKind.Constant:
                categoryField = (uint)_LIBCAT_MEMBERTYPE.LCMT_CONSTANT;
                return true;
 
            case MemberKind.EnumMember:
                categoryField = (uint)_LIBCAT_MEMBERTYPE.LCMT_ENUMITEM;
                return true;
 
            case MemberKind.Event:
                categoryField = (uint)_LIBCAT_MEMBERTYPE.LCMT_EVENT;
                return true;
 
            case MemberKind.Field:
                categoryField = (uint)_LIBCAT_MEMBERTYPE.LCMT_FIELD;
                return true;
 
            case MemberKind.Method:
                categoryField = (uint)_LIBCAT_MEMBERTYPE.LCMT_METHOD;
                return true;
 
            case MemberKind.Operator:
                categoryField = (uint)_LIBCAT_MEMBERTYPE.LCMT_OPERATOR;
                return true;
 
            case MemberKind.Property:
                categoryField = (uint)_LIBCAT_MEMBERTYPE.LCMT_PROPERTY;
                return true;
 
            default:
                Debug.Fail("Unexpected member kind: " + memberListItem.Kind.ToString());
                categoryField = 0;
                return false;
        }
    }
 
    private bool TryGetPhysicalContainerType(uint index, out uint categoryField)
    {
        var listItem = GetListItem(index);
        switch (listItem.ParentListKind)
        {
            case ObjectListKind.Projects:
                categoryField = (uint)_LIBCAT_PHYSICALCONTAINERTYPE.LCPT_PROJECT;
                return true;
 
            case ObjectListKind.References:
                categoryField = (uint)_LIBCAT_PHYSICALCONTAINERTYPE.LCPT_PROJECTREFERENCE;
                return true;
        }
 
        categoryField = 0;
        return false;
    }
 
    private bool TryGetVisibility(uint index, out uint categoryField)
    {
        var item = GetListItem(index);
        categoryField = item.IsHidden
            ? (uint)_LIBCAT_VISIBILITY.LCV_HIDDEN
            : (uint)_LIBCAT_VISIBILITY.LCV_VISIBLE;
 
        return true;
    }
 
    protected override bool TryGetCategoryField(uint index, int category, out uint categoryField)
    {
        switch (category)
        {
            case (int)LIB_CATEGORY.LC_LISTTYPE:
                return TryGetListType(out categoryField);
 
            case (int)_LIB_CATEGORY2.LC_MEMBERINHERITANCE:
                return TryGetMemberInheritance(index, out categoryField);
 
            case (int)LIB_CATEGORY.LC_MEMBERACCESS:
                return TryGetMemberAccess(index, out categoryField);
 
            case (int)LIB_CATEGORY.LC_MEMBERTYPE:
                return TryGetMemberType(index, out categoryField);
 
            case (int)LIB_CATEGORY.LC_CLASSACCESS:
                return TryGetClassAccess(index, out categoryField);
 
            case (int)LIB_CATEGORY.LC_CLASSTYPE:
                return TryGetClassType(index, out categoryField);
 
            case (int)_LIB_CATEGORY2.LC_HIERARCHYTYPE:
                if (Kind == ObjectListKind.Hierarchy)
                {
                    categoryField = this.ParentKind == ObjectListKind.Projects
                        ? (uint)_LIBCAT_HIERARCHYTYPE.LCHT_PROJECTREFERENCES
                        : (uint)_LIBCAT_HIERARCHYTYPE.LCHT_BASESANDINTERFACES;
                }
                else
                {
                    categoryField = (uint)_LIBCAT_HIERARCHYTYPE.LCHT_UNKNOWN;
                }
 
                return true;
 
            case (int)LIB_CATEGORY.LC_NODETYPE:
                categoryField = 0;
                return false;
 
            case (int)_LIB_CATEGORY2.LC_PHYSICALCONTAINERTYPE:
                return TryGetPhysicalContainerType(index, out categoryField);
 
            case (int)LIB_CATEGORY.LC_VISIBILITY:
                return TryGetVisibility(index, out categoryField);
        }
 
        throw new NotImplementedException();
    }
 
    protected override void GetDisplayData(uint index, ref VSTREEDISPLAYDATA data)
    {
        var item = GetListItem(index);
        data.Image = item.GlyphIndex;
        data.SelectedImage = item.GlyphIndex;
 
        if (item.IsHidden)
        {
            data.State |= (uint)_VSTREEDISPLAYSTATE.TDS_GRAYTEXT;
        }
    }
 
    protected override bool GetExpandable(uint index, uint listTypeExcluded)
    {
        switch (Kind)
        {
            case ObjectListKind.Hierarchy:
            case ObjectListKind.Namespaces:
            case ObjectListKind.Projects:
            case ObjectListKind.References:
                return true;
 
            case ObjectListKind.BaseTypes:
            case ObjectListKind.Types:
                return IsExpandableType(index);
        }
 
        return false;
    }
 
    private bool IsExpandableType(uint index)
    {
        if (GetListItem(index) is not TypeListItem typeListItem)
        {
            return false;
        }
 
        var compilation = typeListItem.GetCompilation(this.LibraryManager.Workspace);
        if (compilation == null)
        {
            return false;
        }
 
        var typeSymbol = typeListItem.ResolveTypedSymbol(compilation);
 
        // We never show base types for System.Object
        if (typeSymbol.SpecialType == SpecialType.System_Object)
        {
            return false;
        }
 
        if (typeSymbol.TypeKind == TypeKind.Module)
        {
            return false;
        }
 
        if (typeSymbol.TypeKind == TypeKind.Interface && typeSymbol.Interfaces.IsEmpty)
        {
            return false;
        }
 
        if (typeSymbol.BaseType == null)
        {
            return false;
        }
 
        return true;
    }
 
    protected override uint GetItemCount()
        => (uint)_items.Length;
 
    protected override IVsSimpleObjectList2 GetList(uint index, uint listType, uint flags, VSOBSEARCHCRITERIA2[] pobSrch)
    {
        var listItem = GetListItem(index);
 
        // We need to do a little massaging of the list type and parent item in a couple of cases.
        switch (Kind)
        {
            case ObjectListKind.Hierarchy:
                // LLT_USESCLASSES is for displaying base classes and interfaces
                // LLF_PROJREF is for displaying project references
                listType = listType == (uint)_LIB_LISTTYPE.LLT_CLASSES
                    ? (uint)_LIB_LISTTYPE.LLT_USESCLASSES
                    : Helpers.LLT_PROJREF;
 
                // Use the parent of this list as the parent of the new list.
                listItem = listItem.ParentList.ParentListItem;
 
                break;
 
            case ObjectListKind.BaseTypes:
                if (listType == (uint)_LIB_LISTTYPE.LLT_CLASSES)
                {
                    listType = (uint)_LIB_LISTTYPE.LLT_USESCLASSES;
                }
 
                break;
        }
 
        var listKind = Helpers.ListTypeToObjectListKind(listType);
 
        if (Helpers.IsFindSymbol(flags))
        {
            var project = this.LibraryManager.GetProject(listItem.ProjectId);
            if (project == null)
            {
                return null;
            }
 
            var lookInReferences = (flags & ((uint)_VSOBSEARCHOPTIONS.VSOBSO_LOOKINREFS | (uint)_VSOBSEARCHOPTIONS2.VSOBSO_LISTREFERENCES)) != 0;
 
            var projectAndAssemblySet = this.LibraryManager.GetAssemblySet(project, lookInReferences, CancellationToken.None);
            return this.LibraryManager.GetSearchList(listKind, flags, pobSrch, projectAndAssemblySet);
        }
 
        var compilation = listItem.GetCompilation(this.LibraryManager.Workspace);
        if (compilation == null)
        {
            return null;
        }
 
        switch (listKind)
        {
            case ObjectListKind.Types:
                return new ObjectList(ObjectListKind.Types, flags, this, listItem, LibraryManager, this.LibraryManager.GetTypeListItems(listItem, compilation));
            case ObjectListKind.Hierarchy:
                return new ObjectList(ObjectListKind.Hierarchy, flags, this, listItem, LibraryManager, this.LibraryManager.GetFolderListItems(listItem, compilation));
            case ObjectListKind.Namespaces:
                return new ObjectList(ObjectListKind.Namespaces, flags, this, listItem, LibraryManager, this.LibraryManager.GetNamespaceListItems(listItem, compilation));
            case ObjectListKind.Members:
                return new ObjectList(ObjectListKind.Members, flags, this, listItem, LibraryManager, this.LibraryManager.GetMemberListItems(listItem, compilation));
            case ObjectListKind.References:
                return new ObjectList(ObjectListKind.References, flags, this, listItem, LibraryManager, this.LibraryManager.GetReferenceListItems(listItem, compilation));
            case ObjectListKind.BaseTypes:
                return new ObjectList(ObjectListKind.BaseTypes, flags, this, listItem, LibraryManager, this.LibraryManager.GetBaseTypeListItems(listItem, compilation));
        }
 
        throw new NotImplementedException();
    }
 
    protected override object GetBrowseObject(uint index)
    {
        if (GetListItem(index) is SymbolListItem symbolListItem)
        {
            return this.LibraryManager.Workspace.GetBrowseObject(symbolListItem);
        }
 
        return base.GetBrowseObject(index);
    }
 
    protected override bool SupportsNavInfo
    {
        get { return true; }
    }
 
    protected override IVsNavInfo GetNavInfo(uint index)
    {
        var listItem = GetListItem(index);
        if (listItem == null)
        {
            return null;
        }
 
        if (listItem is ProjectListItem projectListItem)
        {
            var project = this.LibraryManager.GetProject(projectListItem.ProjectId);
            if (project != null)
            {
                return this.LibraryManager.LibraryService.NavInfoFactory.CreateForProject(project);
            }
        }
 
        if (listItem is ReferenceListItem referenceListItem)
        {
            return this.LibraryManager.LibraryService.NavInfoFactory.CreateForReference(referenceListItem.MetadataReference);
        }
 
        if (listItem is SymbolListItem symbolListItem)
        {
            return this.LibraryManager.GetNavInfo(symbolListItem, useExpandedHierarchy: IsClassView());
        }
 
        return null;
    }
 
    protected override IVsNavInfoNode GetNavInfoNode(uint index)
    {
        var listItem = GetListItem(index);
 
        var name = listItem.DisplayText;
        var type = Helpers.ObjectListKindToListType(Kind);
 
        if (type == (uint)_LIB_LISTTYPE.LLT_USESCLASSES)
        {
            type = (uint)_LIB_LISTTYPE.LLT_CLASSES;
        }
        else if (type == Helpers.LLT_PROJREF)
        {
            type = (uint)_LIB_LISTTYPE.LLT_PACKAGE;
        }
 
        return new NavInfoNode(name, type);
    }
 
    protected override bool TryLocateNavInfoNode(IVsNavInfoNode pNavInfoNode, out uint index)
    {
        var itemCount = GetItemCount();
        index = 0xffffffffu;
        if (ErrorHandler.Failed(pNavInfoNode.get_Name(out var matchName)))
        {
            return false;
        }
 
        if (ErrorHandler.Failed(pNavInfoNode.get_Type(out _)))
        {
            return false;
        }
 
        var longestMatchedName = string.Empty;
 
        for (uint i = 0; i < itemCount; i++)
        {
            var name = GetText(i, VSTREETEXTOPTIONS.TTO_DISPLAYTEXT);
 
            if (Kind is ObjectListKind.Types or
                ObjectListKind.Namespaces or
                ObjectListKind.Members)
            {
                if (string.Equals(matchName, name, StringComparison.Ordinal))
                {
                    index = i;
                    break;
                }
            }
            else
            {
                if (string.Equals(matchName, name, StringComparison.Ordinal))
                {
                    index = i;
                    break;
                }
                else if (Kind == ObjectListKind.Projects)
                {
                    if (matchName.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        if (longestMatchedName.Length < name.Length)
                        {
                            longestMatchedName = name;
                            index = i;
                        }
                    }
                }
            }
        }
 
        return index != 0xffffffffu;
    }
 
    protected override bool SupportsDescription
    {
        get { return true; }
    }
 
    protected override bool TryFillDescription(uint index, _VSOBJDESCOPTIONS options, IVsObjectBrowserDescription3 description)
    {
        var listItem = GetListItem(index);
 
        return this.LibraryManager.TryFillDescription(listItem, description, options);
    }
 
    protected override bool TryGetProperty(uint index, _VSOBJLISTELEMPROPID propertyId, out object pvar)
    {
        pvar = null;
 
        var listItem = GetListItem(index);
        if (listItem == null)
        {
            return false;
        }
 
        switch (propertyId)
        {
            case _VSOBJLISTELEMPROPID.VSOBJLISTELEMPROPID_FULLNAME:
                pvar = listItem.FullNameText;
                return true;
 
            case _VSOBJLISTELEMPROPID.VSOBJLISTELEMPROPID_HELPKEYWORD:
                if (listItem is SymbolListItem symbolListItem)
                {
                    var project = this.LibraryManager.Workspace.CurrentSolution.GetProject(symbolListItem.ProjectId);
                    if (project != null)
                    {
                        var compilation = project
                            .GetCompilationAsync(CancellationToken.None)
                            .WaitAndGetResult_ObjectBrowser(CancellationToken.None);
 
                        var symbol = symbolListItem.ResolveSymbol(compilation);
                        if (symbol != null)
                        {
                            var helpContextService = project.Services.GetService<IHelpContextService>();
 
                            pvar = helpContextService.FormatSymbol(symbol);
                            return true;
                        }
                    }
                }
 
                return false;
        }
 
        return false;
    }
 
    protected override bool TryCountSourceItems(uint index, out IVsHierarchy hierarchy, out uint itemid, out uint items)
    {
        hierarchy = null;
        itemid = 0;
        items = 0;
 
        var listItem = GetListItem(index);
        if (listItem == null)
        {
            return false;
        }
 
        hierarchy = this.LibraryManager.Workspace.GetHierarchy(listItem.ProjectId);
 
        if (listItem is ProjectListItem)
        {
            itemid = (uint)VSConstants.VSITEMID.Root;
            items = 1;
            return true;
        }
        else if (listItem is SymbolListItem)
        {
            // TODO: Get itemid
 
            items = 1;
            return true;
        }
 
        return false;
    }
 
    protected override string GetText(uint index, VSTREETEXTOPTIONS tto)
        => GetDisplayText(index, tto);
 
    protected override string GetTipText(uint index, VSTREETOOLTIPTYPE eTipType)
        => null;
 
    protected override async Task GoToSourceAsync(uint index, VSOBJGOTOSRCTYPE srcType)
    {
        try
        {
            var operationExecutor = LibraryManager.ComponentModel.GetService<IUIThreadOperationExecutor>();
 
            using var context = operationExecutor.BeginExecute(ServicesVSResources.IntelliSense, EditorFeaturesResources.Navigating, allowCancellation: true, showProgress: false);
 
            var cancellationToken = context.UserCancellationToken;
            if (srcType == VSOBJGOTOSRCTYPE.GS_DEFINITION &&
                GetListItem(index) is SymbolListItem symbolItem &&
                symbolItem.SupportsGoToDefinition)
            {
                var project = this.LibraryManager.Workspace.CurrentSolution.GetProject(symbolItem.ProjectId);
                var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
                var symbol = symbolItem.ResolveSymbol(compilation);
 
                await this.LibraryManager.Workspace.TryGoToDefinitionAsync(symbol, project, cancellationToken).ConfigureAwait(false);
            }
        }
        catch (OperationCanceledException)
        {
        }
        catch (Exception ex) when (FatalError.ReportAndCatch(ex, ErrorSeverity.Critical))
        {
        }
    }
 
    protected override uint GetUpdateCounter()
    {
        switch (Kind)
        {
            case ObjectListKind.Projects:
            case ObjectListKind.References:
                return LibraryManager.PackageVersion;
            case ObjectListKind.BaseTypes:
            case ObjectListKind.Namespaces:
            case ObjectListKind.Types:
                return LibraryManager.ClassVersion;
            case ObjectListKind.Members:
                return LibraryManager.MembersVersion;
            case ObjectListKind.Hierarchy:
                return LibraryManager.ClassVersion | LibraryManager.MembersVersion;
 
            default:
                Debug.Fail("Unsupported object list kind: " + Kind.ToString());
                throw new InvalidOperationException();
        }
    }
 
    protected override bool TryGetContextMenu(uint index, out Guid menuGuid, out int menuId)
    {
        if (GetListItem(index) == null)
        {
            menuGuid = Guid.Empty;
            menuId = 0;
            return false;
        }
 
        menuGuid = VsMenus.guidSHLMainMenu;
 
        // from vsshlids.h
        const int IDM_VS_CTXT_CV_PROJECT = 0x0432;
        const int IDM_VS_CTXT_CV_ITEM = 0x0433;
        const int IDM_VS_CTXT_CV_GROUPINGFOLDER = 0x0435;
        const int IDM_VS_CTXT_CV_MEMBER = 0x0438;
 
        switch (Kind)
        {
            case ObjectListKind.Projects:
                menuId = IDM_VS_CTXT_CV_PROJECT;
                break;
            case ObjectListKind.Members:
                menuId = IDM_VS_CTXT_CV_MEMBER;
                break;
            case ObjectListKind.Hierarchy:
                menuId = IDM_VS_CTXT_CV_GROUPINGFOLDER;
                break;
            default:
                menuId = IDM_VS_CTXT_CV_ITEM;
                break;
        }
 
        return true;
    }
 
    protected override bool SupportsBrowseContainers
    {
        get { return true; }
    }
 
    protected override bool TryFindBrowseContainer(VSCOMPONENTSELECTORDATA data, out uint index)
    {
        index = 0;
        var count = GetItemCount();
 
        for (uint i = 0; i < count; i++)
        {
            var listItem = GetListItem(i);
 
            if (data.type == VSCOMPONENTTYPE.VSCOMPONENTTYPE_ComPlus)
            {
                if (listItem is not ReferenceListItem referenceListItem)
                {
                    continue;
                }
 
                if (referenceListItem.MetadataReference is not PortableExecutableReference metadataReference)
                {
                    continue;
                }
 
                if (string.Equals(data.bstrFile, metadataReference.FilePath, StringComparison.OrdinalIgnoreCase))
                {
                    index = i;
                    return true;
                }
            }
            else
            {
                Debug.Assert(data.type == VSCOMPONENTTYPE.VSCOMPONENTTYPE_Project);
 
                var hierarchy = this.LibraryManager.Workspace.GetHierarchy(listItem.ProjectId);
                if (hierarchy == null)
                {
                    continue;
                }
 
                if (this.LibraryManager.ServiceProvider.GetService(typeof(SVsSolution)) is not IVsSolution vsSolution)
                {
                    return false;
                }
 
                if (ErrorHandler.Failed(vsSolution.GetProjrefOfProject(hierarchy, out var projectRef)))
                {
                    return false;
                }
 
                if (data.bstrProjRef == projectRef)
                {
                    index = i;
                    return true;
                }
            }
        }
 
        return false;
    }
 
    protected override bool TryGetBrowseContainerData(uint index, ref VSCOMPONENTSELECTORDATA data)
    {
        var listItem = GetListItem(index);
 
        if (listItem is ProjectListItem projectListItem)
        {
            var hierarchy = this.LibraryManager.Workspace.GetHierarchy(projectListItem.ProjectId);
            if (hierarchy == null)
            {
                return false;
            }
 
            if (this.LibraryManager.ServiceProvider.GetService(typeof(SVsSolution)) is not IVsSolution vsSolution)
            {
                return false;
            }
 
            if (ErrorHandler.Failed(vsSolution.GetProjrefOfProject(hierarchy, out var projectRef)))
            {
                return false;
            }
 
            var project = this.LibraryManager.Workspace.CurrentSolution.GetProject(projectListItem.ProjectId);
            if (project == null)
            {
                return false;
            }
 
            data.bstrFile = project.FilePath;
            data.bstrProjRef = projectRef;
            data.bstrTitle = projectListItem.DisplayText;
            data.type = VSCOMPONENTTYPE.VSCOMPONENTTYPE_Project;
        }
        else
        {
            if (listItem is not ReferenceListItem referenceListItem)
            {
                return false;
            }
 
            if (referenceListItem.MetadataReference is not PortableExecutableReference portableExecutableReference)
            {
                return false;
            }
 
            var assemblyIdentity = AssemblyIdentityUtils.TryGetAssemblyIdentity(portableExecutableReference.FilePath);
            if (assemblyIdentity == null)
            {
                return false;
            }
 
            data.bstrFile = portableExecutableReference.FilePath;
            data.type = VSCOMPONENTTYPE.VSCOMPONENTTYPE_ComPlus;
 
            data.wFileMajorVersion = (ushort)assemblyIdentity.Version.Major;
            data.wFileMinorVersion = (ushort)assemblyIdentity.Version.Minor;
            data.wFileBuildNumber = (ushort)assemblyIdentity.Version.Build;
            data.wFileRevisionNumber = (ushort)assemblyIdentity.Version.Revision;
        }
 
        return true;
    }
 
    public ObjectListKind Kind { get; }
 
    public ObjectListKind ParentKind
    {
        get
        {
            return _parentList != null
                ? _parentList.Kind
                : ObjectListKind.None;
        }
    }
 
    public ObjectListItem ParentListItem { get; }
}