Mono: Make sure the generated RootNamespace is a valid identifier
This commit is contained in:
parent
b69569415f
commit
5a4475fce3
3 changed files with 204 additions and 1 deletions
|
@ -41,6 +41,7 @@
|
||||||
<Compile Include="Build\BuildSystem.cs" />
|
<Compile Include="Build\BuildSystem.cs" />
|
||||||
<Compile Include="Editor\MonoDevelopInstance.cs" />
|
<Compile Include="Editor\MonoDevelopInstance.cs" />
|
||||||
<Compile Include="Project\ProjectExtensions.cs" />
|
<Compile Include="Project\ProjectExtensions.cs" />
|
||||||
|
<Compile Include="Project\IdentifierUtils.cs" />
|
||||||
<Compile Include="Project\ProjectGenerator.cs" />
|
<Compile Include="Project\ProjectGenerator.cs" />
|
||||||
<Compile Include="Project\ProjectUtils.cs" />
|
<Compile Include="Project\ProjectUtils.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
|
199
modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs
Normal file
199
modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace GodotSharpTools.Project
|
||||||
|
{
|
||||||
|
public static class IdentifierUtils
|
||||||
|
{
|
||||||
|
public static string SanitizeQualifiedIdentifier(string qualifiedIdentifier, bool allowEmptyIdentifiers)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(qualifiedIdentifier))
|
||||||
|
throw new ArgumentException($"{nameof(qualifiedIdentifier)} cannot be empty", nameof(qualifiedIdentifier));
|
||||||
|
|
||||||
|
string[] identifiers = qualifiedIdentifier.Split(new[] { '.' });
|
||||||
|
|
||||||
|
for (int i = 0; i < identifiers.Length; i++)
|
||||||
|
{
|
||||||
|
identifiers[i] = SanitizeIdentifier(identifiers[i], allowEmpty: allowEmptyIdentifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(".", identifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SanitizeIdentifier(string identifier, bool allowEmpty)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(identifier))
|
||||||
|
{
|
||||||
|
if (allowEmpty)
|
||||||
|
return "Empty"; // Default value for empty identifiers
|
||||||
|
|
||||||
|
throw new ArgumentException($"{nameof(identifier)} cannot be empty if {nameof(allowEmpty)} is false", nameof(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifier.Length > 511)
|
||||||
|
identifier = identifier.Substring(0, 511);
|
||||||
|
|
||||||
|
var identifierBuilder = new StringBuilder();
|
||||||
|
int startIndex = 0;
|
||||||
|
|
||||||
|
if (identifier[0] == '@')
|
||||||
|
{
|
||||||
|
identifierBuilder.Append('@');
|
||||||
|
startIndex += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = startIndex; i < identifier.Length; i++)
|
||||||
|
{
|
||||||
|
char @char = identifier[i];
|
||||||
|
|
||||||
|
switch (Char.GetUnicodeCategory(@char))
|
||||||
|
{
|
||||||
|
case UnicodeCategory.UppercaseLetter:
|
||||||
|
case UnicodeCategory.LowercaseLetter:
|
||||||
|
case UnicodeCategory.TitlecaseLetter:
|
||||||
|
case UnicodeCategory.ModifierLetter:
|
||||||
|
case UnicodeCategory.LetterNumber:
|
||||||
|
case UnicodeCategory.OtherLetter:
|
||||||
|
identifierBuilder.Append(@char);
|
||||||
|
break;
|
||||||
|
case UnicodeCategory.NonSpacingMark:
|
||||||
|
case UnicodeCategory.SpacingCombiningMark:
|
||||||
|
case UnicodeCategory.ConnectorPunctuation:
|
||||||
|
case UnicodeCategory.DecimalDigitNumber:
|
||||||
|
// Identifiers may start with underscore
|
||||||
|
if (identifierBuilder.Length > startIndex || @char == '_')
|
||||||
|
identifierBuilder.Append(@char);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifierBuilder.Length == startIndex)
|
||||||
|
{
|
||||||
|
// All characters were invalid so now it's empty. Fill it with something.
|
||||||
|
identifierBuilder.Append("Empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
identifier = identifierBuilder.ToString();
|
||||||
|
|
||||||
|
if (identifier[0] != '@' && IsKeyword(identifier, anyDoubleUnderscore: true))
|
||||||
|
identifier = '@' + identifier;
|
||||||
|
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsKeyword(string value, bool anyDoubleUnderscore)
|
||||||
|
{
|
||||||
|
// Identifiers that start with double underscore are meant to be used for reserved keywords.
|
||||||
|
// Only existing keywords are enforced, but it may be useful to forbid any identifier
|
||||||
|
// that begins with double underscore to prevent issues with future C# versions.
|
||||||
|
if (anyDoubleUnderscore)
|
||||||
|
{
|
||||||
|
if (value.Length > 2 && value[0] == '_' && value[1] == '_' && value[2] != '_')
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_doubleUnderscoreKeywords.Contains(value))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _keywords.Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HashSet<string> _doubleUnderscoreKeywords = new HashSet<string>
|
||||||
|
{
|
||||||
|
"__arglist",
|
||||||
|
"__makeref",
|
||||||
|
"__reftype",
|
||||||
|
"__refvalue",
|
||||||
|
};
|
||||||
|
|
||||||
|
static HashSet<string> _keywords = new HashSet<string>
|
||||||
|
{
|
||||||
|
"as",
|
||||||
|
"do",
|
||||||
|
"if",
|
||||||
|
"in",
|
||||||
|
"is",
|
||||||
|
"for",
|
||||||
|
"int",
|
||||||
|
"new",
|
||||||
|
"out",
|
||||||
|
"ref",
|
||||||
|
"try",
|
||||||
|
"base",
|
||||||
|
"bool",
|
||||||
|
"byte",
|
||||||
|
"case",
|
||||||
|
"char",
|
||||||
|
"else",
|
||||||
|
"enum",
|
||||||
|
"goto",
|
||||||
|
"lock",
|
||||||
|
"long",
|
||||||
|
"null",
|
||||||
|
"this",
|
||||||
|
"true",
|
||||||
|
"uint",
|
||||||
|
"void",
|
||||||
|
"break",
|
||||||
|
"catch",
|
||||||
|
"class",
|
||||||
|
"const",
|
||||||
|
"event",
|
||||||
|
"false",
|
||||||
|
"fixed",
|
||||||
|
"float",
|
||||||
|
"sbyte",
|
||||||
|
"short",
|
||||||
|
"throw",
|
||||||
|
"ulong",
|
||||||
|
"using",
|
||||||
|
"where",
|
||||||
|
"while",
|
||||||
|
"yield",
|
||||||
|
"double",
|
||||||
|
"extern",
|
||||||
|
"object",
|
||||||
|
"params",
|
||||||
|
"public",
|
||||||
|
"return",
|
||||||
|
"sealed",
|
||||||
|
"sizeof",
|
||||||
|
"static",
|
||||||
|
"string",
|
||||||
|
"struct",
|
||||||
|
"switch",
|
||||||
|
"typeof",
|
||||||
|
"unsafe",
|
||||||
|
"ushort",
|
||||||
|
"checked",
|
||||||
|
"decimal",
|
||||||
|
"default",
|
||||||
|
"finally",
|
||||||
|
"foreach",
|
||||||
|
"partial",
|
||||||
|
"private",
|
||||||
|
"virtual",
|
||||||
|
"abstract",
|
||||||
|
"continue",
|
||||||
|
"delegate",
|
||||||
|
"explicit",
|
||||||
|
"implicit",
|
||||||
|
"internal",
|
||||||
|
"operator",
|
||||||
|
"override",
|
||||||
|
"readonly",
|
||||||
|
"volatile",
|
||||||
|
"interface",
|
||||||
|
"namespace",
|
||||||
|
"protected",
|
||||||
|
"unchecked",
|
||||||
|
"stackalloc",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -140,6 +140,9 @@ namespace GodotSharpTools.Project
|
||||||
|
|
||||||
public static ProjectRootElement CreateLibraryProject(string name, out ProjectPropertyGroupElement mainGroup)
|
public static ProjectRootElement CreateLibraryProject(string name, out ProjectPropertyGroupElement mainGroup)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
throw new ArgumentException($"{nameof(name)} cannot be empty", nameof(name));
|
||||||
|
|
||||||
var root = ProjectRootElement.Create();
|
var root = ProjectRootElement.Create();
|
||||||
root.DefaultTargets = "Build";
|
root.DefaultTargets = "Build";
|
||||||
|
|
||||||
|
@ -149,7 +152,7 @@ namespace GodotSharpTools.Project
|
||||||
mainGroup.AddProperty("ProjectGuid", "{" + Guid.NewGuid().ToString().ToUpper() + "}");
|
mainGroup.AddProperty("ProjectGuid", "{" + Guid.NewGuid().ToString().ToUpper() + "}");
|
||||||
mainGroup.AddProperty("OutputType", "Library");
|
mainGroup.AddProperty("OutputType", "Library");
|
||||||
mainGroup.AddProperty("OutputPath", Path.Combine("bin", "$(Configuration)"));
|
mainGroup.AddProperty("OutputPath", Path.Combine("bin", "$(Configuration)"));
|
||||||
mainGroup.AddProperty("RootNamespace", name);
|
mainGroup.AddProperty("RootNamespace", IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true));
|
||||||
mainGroup.AddProperty("AssemblyName", name);
|
mainGroup.AddProperty("AssemblyName", name);
|
||||||
mainGroup.AddProperty("TargetFrameworkVersion", "v4.5");
|
mainGroup.AddProperty("TargetFrameworkVersion", "v4.5");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue