File: Language\CSharpIdentifier.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Text;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.CSharp;
 
namespace Microsoft.AspNetCore.Razor.Language;
 
internal static class CSharpIdentifier
{
    public static string GetClassNameFromPath(string path)
    {
        var span = path.AsSpanOrDefault();
 
        if (span.Length == 0)
        {
            return path;
        }
 
        const string cshtmlExtension = ".cshtml";
 
        if (span.EndsWith(cshtmlExtension.AsSpan(), StringComparison.OrdinalIgnoreCase))
        {
            span = span[..^cshtmlExtension.Length];
        }
 
        return SanitizeIdentifier(span);
    }
 
    public static string SanitizeIdentifier(ReadOnlySpan<char> inputName)
    {
        if (inputName.Length == 0)
        {
            return string.Empty;
        }
 
        using var _ = StringBuilderPool.GetPooledObject(out var builder);
        AppendSanitized(builder, inputName);
        return builder.ToString();
    }
 
    public static void AppendSanitized(StringBuilder builder, ReadOnlySpan<char> inputName)
    {
        if (inputName.Length == 0)
        {
            return;
        }
 
        var firstChar = inputName[0];
        if (!SyntaxFacts.IsIdentifierStartCharacter(firstChar) && SyntaxFacts.IsIdentifierPartCharacter(firstChar))
        {
            builder.SetCapacityIfLarger(builder.Length + inputName.Length + 1);
            builder.Append('_');
        }
        else
        {
            builder.SetCapacityIfLarger(builder.Length + inputName.Length);
        }
 
        for (int i = 0; i < inputName.Length; i++)
        {
            var ch = inputName[i];
            if (SyntaxFacts.IsIdentifierPartCharacter(ch))
            {
                builder.Append(ch);
            }
            else
            {
                // Not a valid identifier part, replace with underscore
                builder.Append('_');
 
                // If this is a high surrogate, skip the low surrogate as well
                // since a surrogate pair represents a single Unicode character
                // and we need to match the identifier mangling in the template
                // engine / VS
                if (char.IsHighSurrogate(ch) && i + 1 < inputName.Length && char.IsLowSurrogate(inputName[i + 1]))
                {
                    i++; // Skip the low surrogate
                }
            }
        }
    }
}