File: Completion\CompletionProviders\DeclarationName\DeclarationNameRecommender.NameGenerator.cs
Web Access
Project: src\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features)
// 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.Collections.Immutable;
using Humanizer;
using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
using Words = System.Collections.Immutable.ImmutableArray<string>;
 
namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers.DeclarationName;
 
internal sealed partial class DeclarationNameRecommender
{
    internal class NameGenerator
    {
        private const char DefaultInterfacePrefix = 'I';
        private const char DefaultGenericParameterPrefix = 'T';
 
        internal static ImmutableArray<Words> GetBaseNames(ITypeSymbol type, bool pluralize)
        {
            var baseName = TryRemoveKnownPrefixes(type);
            using var parts = TemporaryArray<TextSpan>.Empty;
            StringBreaker.AddWordParts(baseName, ref parts.AsRef());
            var result = GetInterleavedPatterns(parts, baseName, pluralize);
 
            return result;
        }
 
        internal static ImmutableArray<Words> GetBaseNames(IAliasSymbol alias)
        {
            var name = alias.Name;
            if (alias.Target.IsType &&
                ((INamedTypeSymbol)alias.Target).IsInterfaceType() &&
                CanRemovePrefix(name, DefaultInterfacePrefix))
            {
                name = name[1..];
            }
 
            using var breaks = TemporaryArray<TextSpan>.Empty;
            StringBreaker.AddWordParts(name, ref breaks.AsRef());
            var result = GetInterleavedPatterns(breaks, name, pluralize: false);
            return result;
        }
 
        private static ImmutableArray<Words> GetInterleavedPatterns(
            in TemporaryArray<TextSpan> breaks, string baseName, bool pluralize)
        {
            using var result = TemporaryArray<Words>.Empty;
            var breakCount = breaks.Count;
            result.Add(GetWords(0, breakCount, breaks, baseName, pluralize));
 
            for (var length = breakCount - 1; length > 0; length--)
            {
                // going forward
                result.Add(GetLongestForwardSubsequence(length, breaks, baseName, pluralize));
 
                // going backward
                result.Add(GetLongestBackwardSubsequence(length, breaks, baseName, pluralize));
            }
 
            return result.ToImmutableAndClear();
        }
 
        private static Words GetLongestBackwardSubsequence(int length, in TemporaryArray<TextSpan> breaks, string baseName, bool pluralize)
        {
            var breakCount = breaks.Count;
            var start = breakCount - length;
            return GetWords(start, breakCount, breaks, baseName, pluralize);
        }
 
        private static Words GetLongestForwardSubsequence(int length, in TemporaryArray<TextSpan> breaks, string baseName, bool pluralize)
            => GetWords(0, length, breaks, baseName, pluralize);
 
        private static Words GetWords(int start, int end, in TemporaryArray<TextSpan> breaks, string baseName, bool pluralize)
        {
            using var result = TemporaryArray<string>.Empty;
            // Add all the words but the last one
            for (; start < end; start++)
            {
                var @break = breaks[start];
                var text = baseName.Substring(@break.Start, @break.Length);
                if (pluralize && start == end - 1)
                {
                    // Pluralize the last word if necessary
                    result.Add(text.Pluralize());
                }
                else
                {
                    result.Add(text);
                }
            }
 
            return result.ToImmutableAndClear();
        }
 
        //Tries to remove "I" prefix from interfaces and "T" prefix from generic parameter names
        private static string TryRemoveKnownPrefixes(ITypeSymbol type)
        {
            var name = type.Name;
 
            if (type.TypeKind == TypeKind.Interface)
            {
                if (CanRemovePrefix(name, DefaultInterfacePrefix))
                {
                    return name[1..];
                }
            }
 
            if (type.TypeKind == TypeKind.TypeParameter)
            {
                if (CanRemovePrefix(name, DefaultGenericParameterPrefix))
                {
                    return name[1..];
                }
                else if (name is [DefaultGenericParameterPrefix])
                {
                    return ITypeSymbolExtensions.DefaultParameterName;
                }
            }
 
            return type.CreateParameterName();
        }
    }
 
    private static bool CanRemovePrefix(string name, char prefix) => name.Length > 1 && name[0] == prefix && char.IsUpper(name[1]);
}