Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly convert SCREAMING_SNAKE_CASE #1884

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/Generator.Tests/Passes/TestPasses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,39 @@ public void TestCaseRenamePass()

var method = c.Method("lowerCaseMethod");
var property = c.Properties.Find(p => p.Name == "lowerCaseField");
var @enum = c.FindEnum("SCREAMING_ENUM");
var enumItem = @enum.Items.Find(i => i.Name == "snake_value");

passBuilder.RenameDeclsUpperCase(RenameTargets.Any);
passBuilder.RunPasses(pass => pass.VisitASTContext(AstContext));

Assert.That(method.Name, Is.EqualTo("LowerCaseMethod"));
Assert.That(property.Name, Is.EqualTo("LowerCaseField"));
Assert.That(@enum.Name, Is.EqualTo("ScreamingEnum"));
Assert.That(enumItem.Name, Is.EqualTo("SnakeValue"));

AssertConversion("test_123_123", "Test123_123");
AssertConversion("SCREAMING", "Screaming");
AssertConversion("Not_Screaming", "NotScreaming");
AssertConversion("Still_Not___Screaming", "StillNot___Screaming");
AssertConversion("Still_Not___screaming", "StillNot___screaming");
AssertConversion("still_not___screaming", "StillNot___screaming");
AssertConversion("_D_", "_D_");
AssertConversion("_d_", "_d_");
AssertConversion("MyCool2d_string", "MyCool2dString");
AssertConversion("MyCool2_d_String", "MyCool2D_String");
AssertConversion("MyCoolD_d_String", "MyCoolD_dString");
AssertConversion("MyC_de_cool", "MyC_deCool");
AssertConversion("m_d_d_d", "M_dD_d");
AssertConversion("m_d_d_d", "mD_dD", RenameCasePattern.LowerCamelCase);
AssertConversion("__m_d_d_d", "__mD_dD");
AssertConversion("mProperty", "MProperty");
AssertConversion("mProperty", "mProperty", RenameCasePattern.LowerCamelCase);
AssertConversion("m_property", "mProperty", RenameCasePattern.LowerCamelCase);

void AssertConversion(string input, string output, RenameCasePattern pattern = RenameCasePattern.UpperCamelCase)
=> Assert.That(CaseRenamePass.ConvertCaseString(new Class { Name = input }, pattern),
Is.EqualTo(output));

Type.TypePrinterDelegate -= TypePrinterDelegate;
}
Expand Down
56 changes: 33 additions & 23 deletions src/Generator/Passes/RenamePass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -400,12 +400,40 @@ public static string ConvertCaseString(Declaration decl, RenameCasePattern patte
property.GetMethod.SynthKind == FunctionSynthKind.InterfaceInstance)
return decl.Name;

var sb = new StringBuilder(decl.Name);
// check if it's been renamed to avoid a keyword
if (sb[0] == '@' || sb[0] == '$')
sb.Remove(0, 1);
var sb = new StringBuilder(decl.Name.Length);
var startIndex = decl.Name[0] is '@' or '$' ? 1 : 0;
var justHadSeparator = true;
for (var i = startIndex; i < decl.Name.Length; i++)
{
var c = decl.Name[i];
char? prev = i > 0 ? decl.Name[i - 1] : null;
char? next = i < decl.Name.Length - 1 ? decl.Name[i + 1] : null;

if (c == '_'
&& next != null
// Prevent two capitals in a row caused by later uppercasing of first char.
&& (i - 1 != startIndex || pattern == RenameCasePattern.LowerCamelCase)
// Ignore multiple underscores in a row.
&& prev != '_' && next != '_'
// Don't join digits.
&& (IsLetter(prev) || IsLetter(next))
&& !justHadSeparator)
{
sb.Append(char.ToUpperInvariant(next.Value));
i++;
justHadSeparator = true;
continue;
}
// CamelCase separator.
justHadSeparator = IsLower(prev) && IsUpper(c);

RemoveUnderscores(sb);
sb.Append(IsUpper(prev) ? char.ToLowerInvariant(c) : c);

// Nullable helper functions.
bool IsLetter(char? ch) => IsUpper(ch) || IsLower(ch);
bool IsUpper(char? ch) => ch is >= 'A' and <= 'Z';
bool IsLower(char? ch) => ch is >= 'a' and <= 'z';
}

var @class = decl as Class;
switch (pattern)
Expand All @@ -426,24 +454,6 @@ public static string ConvertCaseString(Declaration decl, RenameCasePattern patte

return sb.ToString();
}

private static void RemoveUnderscores(StringBuilder sb)
{
for (int i = sb.Length - 1; i >= 0; i--)
{
if (sb[i] != '_' ||
// lower case intentional if the first character is already upper case
(i + 1 < sb.Length && char.IsLower(sb[i + 1]) && char.IsUpper(sb[0])) ||
// don't end up with more capitals or digits in a row than before
(i > 0 && (char.IsUpper(sb[i - 1]) ||
(i < sb.Length - 1 && char.IsDigit(sb[i + 1]) && char.IsDigit(sb[i - 1])))))
continue;

if (i < sb.Length - 1)
sb[i + 1] = char.ToUpperInvariant(sb[i + 1]);
sb.Remove(i, 1);
}
}
}

public static class RenamePassExtensions
Expand Down
4 changes: 4 additions & 0 deletions tests/dotnet/Native/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ struct TestRename
{
int lowerCaseMethod();
int lowerCaseField;
enum SCREAMING_ENUM
{
snake_value
};
};

/// <summary>A simple test.</summary>
Expand Down
Loading