Skip to content

ToTitleCase keeps an all-caps word unchanged when it's joined by '-', '.' or a tab: "foo-BAR" → "Foo-BAR" but "FOO-BAR" → "Foo-Bar" #82

Description

@matt-edmondson

What's wrong

CaseConverter/CaseConverter.cs:214-231, LowercaseAllCapsWords, splits the input only on ' ':

string[] words = input.Split(' ');
...
builder.Append(IsAllCaps(word) ? word.ToLowerInvariant() : word);

TextInfo.ToTitleCase treats -, . and all whitespace, including tabs, as word boundaries, and it keeps any all-caps word it finds as an acronym. When a space-separated token such as foo-BAR contains lowercase letters, it isn't lowercased first, so TextInfo keeps BAR as is.

Reproduction

Input Actual Expected (per-word rule)
"foo-BAR" "Foo-BAR" "Foo-Bar"
"FOO-BAR" "Foo-Bar" same
"API-key" "API-Key" "Api-Key"
"foo.BAR" "Foo.BAR" "Foo.Bar"
"HELLO\tworld" "HELLO\tWorld" "Hello\tWorld"
"HELLO world" "Hello World" same

The ToTitleCase docs and commit 0b9154a ("Normalize an all-caps word per word, not per string") promise that a word converts the same way whatever else is in the string. These cases break that promise: the result for BAR depends on whether the text next to it contains lowercase letters, and on whether the separator is a space or a tab. Reproduced with MSTest asserts.

Suggested fix

In LowercaseAllCapsWords, split on the same separators TextInfo.ToTitleCase uses: any whitespace, -, ., and so on. Or lowercase each maximal run of letters and apostrophes that is entirely uppercase, instead of each space-delimited token.

Acceptance: every row above produces the expected output, and the existing ToTitleCase tests (including the punctuation cases from d8bdab2) still pass.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions