-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathNSString+IATitlecase.m
More file actions
82 lines (70 loc) · 3.99 KB
/
Copy pathNSString+IATitlecase.m
File metadata and controls
82 lines (70 loc) · 3.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//
// NSString+IATitlecase.m
// Titlecase
//
// Copyright © 2016 Information Architects Inc. All rights reserved.
//
#import "NSString+IATitleCase.h"
#import "IATitlecaseRegularExpressionManager.h"
@interface NSString (IATitlecaseAdditions)
@property (readonly) NSString *firstCharacterCapitalizedString;
@end
@implementation NSMutableString (IATitlecaseExpression)
- (void)replaceMatchesForRegularExpression:(NSRegularExpression *)expression usingTransforms:(NSDictionary<NSNumber *, NSString *(^)(NSString *, NSInteger *)> *)transforms {
[expression enumerateMatchesInString:self options:0 range:NSMakeRange(0, self.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
for (NSInteger matchRangeIndex = 0; matchRangeIndex < result.numberOfRanges; matchRangeIndex++) {
NSRange range = [result rangeAtIndex:matchRangeIndex];
if (range.location == NSNotFound) {
continue;
}
NSString *(^transform)(NSString *word, NSInteger *next) = transforms[@(matchRangeIndex)];
if (transform == nil) {
continue;
}
NSString *substring = [self substringWithRange:range];
NSString *modifiedSubstring = transform(substring, &matchRangeIndex);
if (substring.length != modifiedSubstring.length) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Modified string must have the same length as original." userInfo:@{}];
}
[self replaceCharactersInRange:range withString:modifiedSubstring];
}
}];
}
@end
#define transform(expression) ^NSString *(NSString *word, NSInteger *next){ expression }
@implementation NSString (IATitlecase)
- (NSString *)titlecaseString {
// Regular expressions and their processing are based on the particular revision of Perl script with one difference: white space is not stripped, because I want the NSString category act similarly to -(uppercase|lowercase|capitalized)String.
// https://gist.github.com/gruber/9f9e8650d68b13ce4d78/d7d64ccbc6e1c86b0aae5cb368ea1f6f7f3738c5
IATitlecaseRegularExpressionManager *manager = [IATitlecaseRegularExpressionManager sharedManager];
NSMutableString *titlecaseString = [([self.uppercaseString isEqualToString:self] ? self.lowercaseString : self) mutableCopy];
[titlecaseString replaceMatchesForRegularExpression:manager.titlecaseExpression usingTransforms:@{
@1: transform( return word; ),
@2: transform( *next = 6; return word; ),
@3: transform( *next = 6; return word.lowercaseString; ),
@4: transform( *next = 6; return word.lowercaseString.firstCharacterCapitalizedString; ),
@5: transform( return word; ),
@6: transform( return word; ),
}];
[titlecaseString replaceMatchesForRegularExpression:manager.startExceptionExpression usingTransforms:@{
@1: transform( return word; ),
@2: transform( return word.lowercaseString.firstCharacterCapitalizedString; ),
}];
[titlecaseString replaceMatchesForRegularExpression:manager.endExceptionExpression usingTransforms:@{
@1: transform( return word.lowercaseString.firstCharacterCapitalizedString; ),
}];
[titlecaseString replaceMatchesForRegularExpression:manager.startHyphenatedCompoundExpression usingTransforms:@{
@1: transform( return word.lowercaseString.firstCharacterCapitalizedString; ),
}];
[titlecaseString replaceMatchesForRegularExpression:manager.endHyphenatedCompoundExpression usingTransforms:@{
@1: transform( return word; ),
@2: transform( return word.firstCharacterCapitalizedString; ),
}];
return titlecaseString;
}
- (NSString *)firstCharacterCapitalizedString {
NSRange firstCharacterRange = [self rangeOfComposedCharacterSequenceAtIndex:0];
NSString *firstCharacter = [self substringWithRange:firstCharacterRange];
return [self stringByReplacingCharactersInRange:firstCharacterRange withString:firstCharacter.uppercaseString];
}
@end