diff --git a/Sources/Spices/Internal/String+Helpers.swift b/Sources/Spices/Internal/String+Helpers.swift index c7be00d..70522ff 100644 --- a/Sources/Spices/Internal/String+Helpers.swift +++ b/Sources/Spices/Internal/String+Helpers.swift @@ -13,15 +13,26 @@ extension String { var pieces = [String]() var currentPiece = "" - for (idx, character) in enumerated() { - if idx == 0 { + for (idx, character) in zip(indices, self) { + if idx == startIndex { currentPiece += character.uppercased() } else if character.isUppercase { // Small check: recognize runs of multiple uppercase letters and - // consider them part of the same word. - if let previousChar = currentPiece.last, previousChar.isUppercase { + // consider them part of the same word until the start of the next word. + let previousIndex = index(before: idx) + let nextIndex = index(after: idx) + let previous = self[previousIndex] + let next = nextIndex < endIndex ? self[nextIndex] : nil + if previous.isUppercase && next?.isLowercase == true { + // Previous word was an initialism, and this uppercase letter starts a new word + // containing the next character. + pieces.append(currentPiece) + currentPiece = String(character) + } else if previous.isUppercase { + // Continue the initialism. currentPiece.append(character) } else { + // Previous word was not an initialism, start a new word. pieces.append(currentPiece) currentPiece = character.uppercased() } @@ -30,7 +41,7 @@ extension String { } } - // Commit the last bit of the phrase + // Commit the last bit of the phrase. if !currentPiece.isEmpty { pieces.append(currentPiece) } diff --git a/Tests/SpicesTests/CamelCaseToNaturalTextTests.swift b/Tests/SpicesTests/CamelCaseToNaturalTextTests.swift index 3144c1f..e8365a6 100644 --- a/Tests/SpicesTests/CamelCaseToNaturalTextTests.swift +++ b/Tests/SpicesTests/CamelCaseToNaturalTextTests.swift @@ -15,6 +15,7 @@ struct CamelCaseToNaturalTextTests { #expect("featureFlags".camelCaseToNaturalText() == "Feature Flags") #expect("notifications".camelCaseToNaturalText() == "Notifications") #expect("fastRefreshWidgets".camelCaseToNaturalText() == "Fast Refresh Widgets") + #expect("ignoreNextHTTPRequest".camelCaseToNaturalText() == "Ignore Next HTTP Request") // Suboptimal, but heuristics for these would be annoying. #expect("httpVersion".camelCaseToNaturalText() == "Http Version")