File: Structure\StructureTag.cs
Web Access
Project: ..\..\..\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 Microsoft.CodeAnalysis.Structure;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Adornments;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.Implementation.Structure
{
    internal sealed class StructureTag : IStructureTag, IEquatable<StructureTag>
    {
        private readonly AbstractStructureTaggerProvider _tagProvider;
 
        public StructureTag(AbstractStructureTaggerProvider tagProvider, BlockSpan blockSpan, ITextSnapshot snapshot)
        {
            Snapshot = snapshot;
            OutliningSpan = blockSpan.TextSpan.ToSpan();
            Type = ConvertType(blockSpan.Type);
            IsCollapsible = blockSpan.IsCollapsible;
            IsDefaultCollapsed = blockSpan.IsDefaultCollapsed;
            IsImplementation = blockSpan.AutoCollapse;
 
            if (blockSpan.HintSpan.Start < blockSpan.TextSpan.Start)
            {
                // The HeaderSpan is what is used for drawing the guidelines and also what is shown if
                // you mouse over a guideline. We will use the text from the hint start to the collapsing
                // start; in the case this spans mutiple lines the editor will clip it for us and suffix an
                // ellipsis at the end.
                HeaderSpan = Span.FromBounds(blockSpan.HintSpan.Start, blockSpan.TextSpan.Start);
            }
            else
            {
                var hintLine = snapshot.GetLineFromPosition(blockSpan.HintSpan.Start);
                HeaderSpan = AbstractStructureTaggerProvider.TrimLeadingWhitespace(hintLine.Extent);
            }
 
            CollapsedText = blockSpan.BannerText;
            CollapsedHintFormSpan = blockSpan.HintSpan.ToSpan();
            _tagProvider = tagProvider;
        }
 
        /// <summary>
        /// The contents of the buffer to show if we mouse over the collapsed indicator.
        /// </summary>
        public readonly Span CollapsedHintFormSpan;
 
        public readonly string CollapsedText;
 
        public ITextSnapshot Snapshot { get; }
        public Span? OutliningSpan { get; }
        public Span? HeaderSpan { get; }
        public Span? GuideLineSpan => null;
        public int? GuideLineHorizontalAnchorPoint => null;
        public string Type { get; }
        public bool IsCollapsible { get; }
        public bool IsDefaultCollapsed { get; }
        public bool IsImplementation { get; }
 
        // Editor uses this here:
        // https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_git/VS-Platform?path=/src/Editor/Text/Impl/Structure/StructureSpanningTree/StructureSpanningTree.cs&version=GBmain&line=308&lineEnd=309&lineStartColumn=1&lineEndColumn=1&lineStyle=plain&_a=contents
        public override int GetHashCode()
            => Hash.Combine(this.GuideLineHorizontalAnchorPoint.GetHashCode(),
               Hash.Combine(this.Type,
               Hash.Combine(this.IsCollapsible,
               Hash.Combine(this.IsDefaultCollapsed,
               Hash.Combine(this.IsImplementation,
               Hash.Combine(this.OutliningSpan.GetHashCode(),
               Hash.Combine(this.HeaderSpan.GetHashCode(), this.GuideLineSpan.GetHashCode())))))));
 
        public override bool Equals(object? obj)
            => Equals(obj as StructureTag);
 
        public bool Equals(StructureTag? other)
        {
            return other != null &&
                this.GuideLineHorizontalAnchorPoint == other.GuideLineHorizontalAnchorPoint &&
                this.Type == other.Type &&
                this.IsCollapsible == other.IsCollapsible &&
                this.IsDefaultCollapsed == other.IsDefaultCollapsed &&
                this.IsImplementation == other.IsImplementation &&
                _tagProvider.SpanEquals(this.Snapshot, this.OutliningSpan, other.Snapshot, other.OutliningSpan) &&
                _tagProvider.SpanEquals(this.Snapshot, this.HeaderSpan, other.Snapshot, other.HeaderSpan) &&
                _tagProvider.SpanEquals(this.Snapshot, this.GuideLineSpan, other.Snapshot, other.GuideLineSpan);
        }
 
        public object? GetCollapsedForm()
        {
            return CollapsedText;
        }
 
        public object? GetCollapsedHintForm()
        {
            return _tagProvider.GetCollapsedHintForm(this);
        }
 
        private static string ConvertType(string type)
        {
            return type switch
            {
                BlockTypes.Conditional => PredefinedStructureTagTypes.Conditional,
                BlockTypes.Comment => PredefinedStructureTagTypes.Comment,
                BlockTypes.Expression => PredefinedStructureTagTypes.Expression,
                BlockTypes.Imports => PredefinedStructureTagTypes.Imports,
                BlockTypes.Loop => PredefinedStructureTagTypes.Loop,
                BlockTypes.Member => PredefinedStructureTagTypes.Member,
                BlockTypes.Namespace => PredefinedStructureTagTypes.Namespace,
                BlockTypes.Nonstructural => PredefinedStructureTagTypes.Nonstructural,
                BlockTypes.PreprocessorRegion => PredefinedStructureTagTypes.PreprocessorRegion,
                BlockTypes.Statement => PredefinedStructureTagTypes.Statement,
                BlockTypes.Type => PredefinedStructureTagTypes.Type,
                _ => PredefinedStructureTagTypes.Structural
            };
        }
    }
}