File: Extensibility\NavigationBar\WrappedNavigationBarItem.cs
Web Access
Project: src\src\EditorFeatures\Core\Microsoft.CodeAnalysis.EditorFeatures.csproj (Microsoft.CodeAnalysis.EditorFeatures)
// 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.Collections.Immutable;
using Microsoft.CodeAnalysis.NavigationBar;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
 
namespace Microsoft.CodeAnalysis.Editor;
 
/// <summary>
/// Implementation of the editor layer <see cref="NavigationBarItem"/> that wraps a feature layer <see cref="RoslynNavigationBarItem"/>
/// </summary>
// We suppress this as this type *does* override ComputeAdditionalHashCodeParts
internal sealed class WrappedNavigationBarItem : NavigationBarItem, IEquatable<WrappedNavigationBarItem>
{
    public readonly RoslynNavigationBarItem UnderlyingItem;
 
    internal WrappedNavigationBarItem(ITextVersion textVersion, RoslynNavigationBarItem underlyingItem)
        : base(
              textVersion,
              underlyingItem.Text,
              underlyingItem.Glyph,
              GetSpans(underlyingItem),
              underlyingItem.ChildItems.SelectAsArray(static (v, textVersion) => (NavigationBarItem)new WrappedNavigationBarItem(textVersion, v), textVersion),
              underlyingItem.Indent,
              underlyingItem.Bolded,
              underlyingItem.Grayed)
    {
        UnderlyingItem = underlyingItem;
    }
 
    private static ImmutableArray<TextSpan> GetSpans(RoslynNavigationBarItem underlyingItem)
    {
        using var _ = ArrayBuilder<TextSpan>.GetInstance(out var spans);
        AddSpans(underlyingItem, spans);
        spans.SortAndRemoveDuplicates(Comparer<TextSpan>.Default);
        return spans.ToImmutableAndClear();
 
        static void AddSpans(RoslynNavigationBarItem underlyingItem, ArrayBuilder<TextSpan> spans)
        {
            // For a regular symbol we want to select it if the user puts their caret in any of the spans of it in this file.
            if (underlyingItem is RoslynNavigationBarItem.SymbolItem { Location.InDocumentInfo.spans: var symbolSpans })
            {
                spans.AddRange(symbolSpans);
            }
            else if (underlyingItem is RoslynNavigationBarItem.ActionlessItem)
            {
                // An actionless item represents something that exists just to show a child-list, but should otherwise
                // not navigate or cause anything to be generated.  However, we still want to automatically select it
                // whenever the user puts their caret in any of the spans of its child items in this file.
                //
                // For example, in VB any withevents members will be put in the type-list, and the events those members
                // are hooked up to will then be in the member-list.  In this case, we want moving into the span of that
                // member to select the withevent member in the type-list.
                foreach (var child in underlyingItem.ChildItems)
                    AddSpans(child, spans);
            }
        }
    }
 
    public override bool Equals(object? obj)
        => Equals(obj as WrappedNavigationBarItem);
 
    public bool Equals(WrappedNavigationBarItem? other)
        => base.Equals(other) &&
           UnderlyingItem.Equals(other.UnderlyingItem);
 
    public override int GetHashCode()
        => throw new NotImplementedException();
}