File: SignatureHelp\Presentation\Signature.cs
Web Access
Project: src\src\EditorFeatures\Core.Wpf\Microsoft.CodeAnalysis.EditorFeatures.Wpf_2jb51xel_wpftmp.csproj (Microsoft.CodeAnalysis.EditorFeatures.Wpf)
// 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.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis.SignatureHelp;
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp.Presentation
{
    internal class Signature : ISignature
    {
        private const int MaxParamColumnCount = 100;
 
        private readonly SignatureHelpItem _signatureHelpItem;
 
        public Signature(ITrackingSpan applicableToSpan, SignatureHelpItem signatureHelpItem, int selectedParameterIndex)
        {
            if (selectedParameterIndex < -1 || selectedParameterIndex >= signatureHelpItem.Parameters.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(selectedParameterIndex));
            }
 
            this.ApplicableToSpan = applicableToSpan;
            _signatureHelpItem = signatureHelpItem;
            _parameterIndex = selectedParameterIndex;
        }
 
        private bool _isInitialized;
        private void EnsureInitialized()
        {
            if (!_isInitialized)
            {
                _isInitialized = true;
                Initialize();
            }
        }
 
        private Signature InitializedThis
        {
            get
            {
                EnsureInitialized();
                return this;
            }
        }
 
        private IList<TaggedText> _displayParts;
        internal IList<TaggedText> DisplayParts => InitializedThis._displayParts;
 
        public ITrackingSpan ApplicableToSpan { get; }
 
        private string _content;
        public string Content => InitializedThis._content;
 
        private readonly int _parameterIndex = -1;
        public IParameter CurrentParameter
        {
            get
            {
                EnsureInitialized();
                return _parameterIndex >= 0 && _parameters != null ? _parameters[_parameterIndex] : null;
            }
        }
 
        /// <remarks>
        /// The documentation is included in <see cref="Content"/> so that it will be classified.
        /// </remarks>
        public string Documentation => null;
 
        private ReadOnlyCollection<IParameter> _parameters;
        public ReadOnlyCollection<IParameter> Parameters => InitializedThis._parameters;
 
        private string _prettyPrintedContent;
        public string PrettyPrintedContent => InitializedThis._prettyPrintedContent;
 
        // This event is required by the ISignature interface but it's not actually used
        // (once created the CurrentParameter property cannot change)
        public event EventHandler<CurrentParameterChangedEventArgs> CurrentParameterChanged
        {
            add
            {
            }
            remove
            {
            }
        }
 
        private IList<TaggedText> _prettyPrintedDisplayParts;
        internal IList<TaggedText> PrettyPrintedDisplayParts => InitializedThis._prettyPrintedDisplayParts;
 
        private void Initialize()
        {
            var content = new StringBuilder();
            var prettyPrintedContent = new StringBuilder();
 
            var parts = new List<TaggedText>();
            var prettyPrintedParts = new List<TaggedText>();
 
            var parameters = new List<IParameter>();
 
            var signaturePrefixParts = _signatureHelpItem.PrefixDisplayParts;
            var signaturePrefixContent = _signatureHelpItem.PrefixDisplayParts.GetFullText();
 
            AddRange(signaturePrefixParts, parts, prettyPrintedParts);
            Append(signaturePrefixContent, content, prettyPrintedContent);
 
            var separatorParts = _signatureHelpItem.SeparatorDisplayParts;
            var separatorContent = separatorParts.GetFullText();
 
            var newLinePart = new TaggedText(TextTags.LineBreak, "\r\n");
            var newLineContent = newLinePart.ToString();
            var spacerPart = new TaggedText(TextTags.Space, new string(' ', signaturePrefixContent.Length));
            var spacerContent = spacerPart.ToString();
 
            var paramColumnCount = 0;
 
            for (var i = 0; i < _signatureHelpItem.Parameters.Length; i++)
            {
                var sigHelpParameter = _signatureHelpItem.Parameters[i];
 
                var parameterPrefixParts = sigHelpParameter.PrefixDisplayParts;
                var parameterPrefixContext = sigHelpParameter.PrefixDisplayParts.GetFullText();
 
                var parameterParts = AddOptionalBrackets(
                    sigHelpParameter.IsOptional, sigHelpParameter.DisplayParts);
                var parameterContent = parameterParts.GetFullText();
 
                var parameterSuffixParts = sigHelpParameter.SuffixDisplayParts;
                var parameterSuffixContext = sigHelpParameter.SuffixDisplayParts.GetFullText();
 
                paramColumnCount += separatorContent.Length + parameterPrefixContext.Length + parameterContent.Length + parameterSuffixContext.Length;
 
                if (i > 0)
                {
                    AddRange(separatorParts, parts, prettyPrintedParts);
                    Append(separatorContent, content, prettyPrintedContent);
 
                    if (paramColumnCount > MaxParamColumnCount)
                    {
                        prettyPrintedParts.Add(newLinePart);
                        prettyPrintedParts.Add(spacerPart);
                        prettyPrintedContent.Append(newLineContent);
                        prettyPrintedContent.Append(spacerContent);
 
                        paramColumnCount = 0;
                    }
                }
 
                AddRange(parameterPrefixParts, parts, prettyPrintedParts);
                Append(parameterPrefixContext, content, prettyPrintedContent);
 
                parameters.Add(new Parameter(this, sigHelpParameter, parameterContent, content.Length, prettyPrintedContent.Length));
 
                AddRange(parameterParts, parts, prettyPrintedParts);
                Append(parameterContent, content, prettyPrintedContent);
 
                AddRange(parameterSuffixParts, parts, prettyPrintedParts);
                Append(parameterSuffixContext, content, prettyPrintedContent);
            }
 
            AddRange(_signatureHelpItem.SuffixDisplayParts, parts, prettyPrintedParts);
            Append(_signatureHelpItem.SuffixDisplayParts.GetFullText(), content, prettyPrintedContent);
 
            if (_parameterIndex >= 0)
            {
                var sigHelpParameter = _signatureHelpItem.Parameters[_parameterIndex];
 
                AddRange(sigHelpParameter.SelectedDisplayParts, parts, prettyPrintedParts);
                Append(sigHelpParameter.SelectedDisplayParts.GetFullText(), content, prettyPrintedContent);
            }
 
            AddRange(_signatureHelpItem.DescriptionParts, parts, prettyPrintedParts);
            Append(_signatureHelpItem.DescriptionParts.GetFullText(), content, prettyPrintedContent);
 
            var documentation = _signatureHelpItem.DocumentationFactory(CancellationToken.None).ToList();
            if (documentation.Count > 0)
            {
                AddRange(new[] { newLinePart }, parts, prettyPrintedParts);
                Append(newLineContent, content, prettyPrintedContent);
 
                AddRange(documentation, parts, prettyPrintedParts);
                Append(documentation.GetFullText(), content, prettyPrintedContent);
            }
 
            _content = content.ToString();
            _prettyPrintedContent = prettyPrintedContent.ToString();
            _displayParts = parts.ToImmutableArrayOrEmpty();
            _prettyPrintedDisplayParts = prettyPrintedParts.ToImmutableArrayOrEmpty();
            _parameters = parameters.ToReadOnlyCollection();
        }
 
        private static void AddRange(IList<TaggedText> values, List<TaggedText> parts, List<TaggedText> prettyPrintedParts)
        {
            parts.AddRange(values);
            prettyPrintedParts.AddRange(values);
        }
 
        private static void Append(string text, StringBuilder content, StringBuilder prettyPrintedContent)
        {
            content.Append(text);
            prettyPrintedContent.Append(text);
        }
 
        private static IList<TaggedText> AddOptionalBrackets(bool isOptional, IList<TaggedText> list)
        {
            if (isOptional)
            {
                var result = new List<TaggedText>
                {
                    new TaggedText(TextTags.Punctuation, "[")
                };
                result.AddRange(list);
                result.Add(new TaggedText(TextTags.Punctuation, "]"));
                return result;
            }
 
            return list;
        }
    }
}