Skip to content
Open
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
12 changes: 12 additions & 0 deletions src/Humanizer.Tests/EnumHumanizeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,16 @@ enum DummyEnum
First,
Second
}
[Fact]
public void Humanize_Works_For_Enum_Known_Only_At_Runtime()
{
Enum value = EnumUnderTest.MemberWithoutDescriptionAttribute;

var result = value.Humanize();

Assert.Equal(
EnumTestsResources.MemberWithoutDescriptionAttributeSentence,
result);
}

}
27 changes: 22 additions & 5 deletions src/Humanizer/EnumHumanizeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@
/// <code>
/// enum UserType { AnonymousUser, RegisteredUser }
/// UserType.AnonymousUser.Humanize() => "Anonymous user"
///
///
/// [Flags]
/// enum Permission { None = 0, Read = 1, Write = 2, Delete = 4 }
/// (Permission.Read | Permission.Write).Humanize() => "Read, Write"
///
/// enum Status
/// {
///
/// enum Status
/// {
/// [Description("Currently active")]
/// Active
/// Active
/// }
/// Status.Active.Humanize() => "Currently active"
/// </code>
Expand Down Expand Up @@ -62,6 +62,23 @@
return humanized[input];
}

public static string Humanize(this Enum input)
{
if (input == null)
ArgumentNullException.ThrowIfNull(input);

var enumType = input.GetType();

Check failure on line 70 in src/Humanizer/EnumHumanizeExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze C#

Dereference of a possibly null reference.

Check failure on line 70 in src/Humanizer/EnumHumanizeExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze C#

Dereference of a possibly null reference.

Check failure on line 70 in src/Humanizer/EnumHumanizeExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze C#

Dereference of a possibly null reference.

Check failure on line 70 in src/Humanizer/EnumHumanizeExtensions.cs

View workflow job for this annotation

GitHub Actions / Analyze C#

Dereference of a possibly null reference.

// Get the generic Humanize<T> method
var method = typeof(EnumHumanizeExtensions)
.GetMethod(nameof(Humanize), new[] { enumType });

if (method == null)
throw new InvalidOperationException($"No Humanize method found for enum type {enumType}.");

return (string)method.Invoke(null, new object[] { input })!;
}


/// <summary>
/// Converts an enum value to a human-readable string with the specified letter casing applied.
Expand Down
36 changes: 34 additions & 2 deletions src/Humanizer/Transformer/ToTitleCase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public string Transform(string input) =>
#if NET7_0_OR_GREATER
[GeneratedRegex(WordPattern)]
private static partial Regex WordRegexGenerated();

private static Regex WordRegex() => WordRegexGenerated();
#else
private static readonly Regex WordRegexDefinition = new(WordPattern, RegexOptions.Compiled);
Expand All @@ -26,11 +26,19 @@ public string Transform(string input, CultureInfo culture)
foreach (Match word in matches)
{
var value = word.Value;
if (AllCapitals(value) || IsArticleOrConjunctionOrPreposition(value))
var isSentenceStart = IsSentenceStart(input, word.Index);

if (AllCapitals(value))
{
continue;
}

if (IsArticleOrConjunctionOrPreposition(value) && !isSentenceStart)
{
continue;
}


builder[word.Index] = textInfo.ToUpper(value[0]);
Overwrite(builder, word.Index + 1, textInfo.ToLower(value[1..]));
}
Expand Down Expand Up @@ -71,4 +79,28 @@ word is

// prepositions
"as" or "at" or "by" or "for" or "in" or "of" or "off" or "on" or "to" or "up" or "via";


static bool IsSentenceStart(string input, int wordIndex)
{
if (wordIndex == 0)
{
return true;
}

for (var i = wordIndex - 1; i >= 0; i--)
{
var ch = input[i];

if (char.IsWhiteSpace(ch))
{
continue;
}

return ch is '.' or '!' or '?';
Comment on lines +96 to +100

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat words after closing quotes/parens as sentence starts

The new sentence-start detection only looks at the first non-whitespace character before the word, so if a sentence ends with punctuation followed by a closing quote or parenthesis (e.g., "Hello." the or "Hello." (the ...)) the previous non-whitespace character becomes "/), not ., !, or ?. In those cases IsSentenceStart returns false and articles/conjunctions/prepositions right after a real sentence boundary remain lowercase, which regresses the intended behavior for quoted or parenthesized sentences.

Useful? React with 👍 / 👎.

}

return true;
}

}
Loading