Normalize an all-caps word per word, not per string - #73
Merged
Merged
Conversation
ToTitleCase tested IsAllCaps against the whole string, so an all-caps word was normalized only when nothing else in the string was lowercase. ToPascalCase and ToCamelCase inherit that through it, so the same token converted two different ways depending on its neighbours: "MAX_SIZE" gave "MaxSize" but "set MAX_SIZE" gave "SetMAXSIZE", and "URL" gave "Url" but "my URL handler" gave "MyURLHandler". The macro and snake paths never had the dependence. Lowercase each all-caps word individually before handing the string to TextInfo.ToTitleCase. A word now converts the same way whatever else is in the string, and the title-case-derived converters agree with the macro/snake ones on word boundaries. This changes output for existing callers: an all-caps word beside a lowercase one is now normalized rather than preserved as an acronym, so "parse HTTP header" Pascal-cases to "ParseHttpHeader". That is the direction README.md already documented - it shows "API_response_URL" giving "ApiResponseUrl", which the library did not actually produce until now - and it matches .NET naming guidance. The two tests that pinned "FOX" surviving in a mixed string are updated, and a test pins the README example. Fixes #72 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WDrz1L5y2hSRD2xci1QNDh
|
This was referenced Sep 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #72
The choice
The issue offers three options and deliberately does not pick one, while stating the acceptance criteria neutrally ("under whichever of the options above is chosen"). This takes option 1 — normalize per word — for three reasons:
ToTitleCase) leavesToTitleCaseitself neighbour-dependent:URL→Urlbutmy URL handler→My URL Handler.MAX_SIZE→MAXSIZE, losing the word boundary the macro/snake path already gets right.HttpHeader, notHTTPHeader).There is a fourth argument the issue does not mention, found while measuring:
README.mdalready documents option 1's answer. Its acronym example claims"API_response_URL".ToPascalCase()gives"ApiResponseUrl". Measured atmain(5c3fa37), it actually gives"APIResponseURL". The documented contract and the code disagreed, and this change resolves it in the documentation's favour.The change
ToTitleCasetestedIsAllCapsagainst the whole string, so one lowercase character anywhere stopped every all-caps word from being normalized.LowercaseAllCapsWordsnow makes that decision per space-separated word before handing the string toTextInfo.ToTitleCase.ToPascalCaseandToCamelCaseinherit the fix throughToTitleCase;ToMacroCase/ToSnakeCase/ToKebabCaseare untouched because they never had the defect.Measured at
main(5c3fa37) and on this branch, .NET SDK 10.0.401, Linux:ToTitleCaseToPascalCaseToCamelCaseToMacroCaseMAX_SIZEMax _ SizeMaxSizemaxSizeMAX_SIZEset MAX_SIZESet MAX _ SIZE→Set Max _ SizeSetMAXSIZE→SetMaxSizesetMAXSIZE→setMaxSizeSET_MAX_SIZEget MAX_SIZEGet MAX _ SIZE→Get Max _ SizeGetMAXSIZE→GetMaxSizegetMAXSIZE→getMaxSizeGET_MAX_SIZEURLUrlUrlurlURLmy URL handlerMy URL Handler→My Url HandlerMyURLHandler→MyUrlHandlermyURLHandler→myUrlHandlerMY_URL_HANDLERHTTPHttpHttphttpHTTPparse HTTP headerParse HTTP Header→Parse Http HeaderParseHTTPHeader→ParseHttpHeaderparseHTTPHeader→parseHttpHeaderPARSE_HTTP_HEADERAPI_response_URLAPIResponseURL→ApiResponseUrlaPIResponseURL→apiResponseUrlRows where nothing changed are the single-word cases, which already normalized. Every column now reads the same for a word alone and beside a lowercase one.
The rule is stated in the XML docs on
ToTitleCase,ToPascalCaseandToCamelCase, since it is not inferable from the method names.This changes output for existing callers
An all-caps word beside a lowercase one is now normalized rather than preserved as an acronym. Two existing tests pinned the old behaviour and are updated, exactly as the issue anticipates for this option:
ToTitleCaseShouldHandleMultipleSpaces—" the quick brown FOX "→"The Quick Brown FOX"becomes"The Quick Brown Fox"ToTitleCaseShouldConvertToTitleCase—"the quick Brown FOX"→ same changeTagged
[minor]on the commit subject accordingly.Tests
Five added. Each of the first three measures the word alone and beside a lowercase one, so a regression to whole-string reasoning fails on the second assertion while the first still passes — which is the shape of the defect:
ToTitleCaseShouldNormalizeAnAllCapsWordIndependentlyOfItsNeighboursHTTP/parse HTTP headerToPascalCaseShouldNormalizeAnAllCapsWordIndependentlyOfItsNeighboursMAX_SIZE/set MAX_SIZEToCamelCaseShouldNormalizeAnAllCapsWordIndependentlyOfItsNeighboursURL/my URL handlerToPascalCaseShouldMatchTheAcronymExampleInTheReadmeREADME.mdexample that had driftedToPascalCaseShouldAgreeWithToMacroCaseOnWordBoundariesset MAX_SIZEProved failing without the fix. Reverting
CaseConverter.cstomainand keeping the new tests: 34 total, 6 failed — all four new tests plus the two updatedFOXtests. Each new test failed on its second assertion, confirming the neighbour dependence rather than a blanket wrong answer.Verification
dotnet build CaseConverter.sln -c Release— succeeded, 0 warnings, 0 errors acrossnet10.0,net9.0,net8.0,netstandard2.1,netstandard2.0(analyzers run as errors in this repo)dotnet test CaseConverter.Test -c Release— 34 total, 34 passed, 0 failedNot in this change
README.mdneeds no edit — its acronym example is now correct rather than aspirational, and a test pins it.CLAUDE.md's "Regex-Based String Processing" section is stale for an unrelated reason (the regexes were replaced by code-point walks in the BMP fix), and is left alone rather than folded in here.🤖 Generated with Claude Code
https://claude.ai/code/session_01WDrz1L5y2hSRD2xci1QNDh
Generated by Claude Code