forked from git-up/GitUp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGIFunctions.m
More file actions
executable file
·184 lines (166 loc) · 8.57 KB
/
GIFunctions.m
File metadata and controls
executable file
·184 lines (166 loc) · 8.57 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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Copyright (C) 2015 Pierre-Olivier Latour <[email protected]>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#if !__has_feature(objc_arc)
#error This file requires ARC
#endif
#import "GIPrivate.h"
#define TEST_BITS(c, m) ((c & (m)) == (m))
void GIComputeHighlightRanges(const char* deletedBytes, NSUInteger deletedCount, CFIndex deletedLength, CFRange* deletedRange, const char* addedBytes, NSUInteger addedCount, CFIndex addedLength, CFRange* addedRange) {
const char* deletedMin = deletedBytes;
const char* deletedMax = deletedBytes + deletedCount;
const char* addedMin = addedBytes;
const char* addedMax = addedBytes + addedCount;
CFIndex start = 0;
size_t remaining = 0;
while ((deletedMin < deletedMax) && (addedMin < addedMax)) {
if (*deletedMin != *addedMin) {
break;
}
if (remaining == 0) {
unsigned char byte = *(unsigned char*)deletedMin;
if (TEST_BITS(byte, 0b11000000)) {
remaining = 2;
} else if (TEST_BITS(byte, 0b11100000)) {
remaining = 3;
} else if (TEST_BITS(byte, 0b11110000)) {
remaining = 4;
} else if (TEST_BITS(byte, 0b11111000)) {
remaining = 5;
} else if (TEST_BITS(byte, 0b11111100)) {
remaining = 6;
} else {
XLOG_DEBUG_CHECK(!(byte & (1 << 7)));
remaining = 1;
}
}
++deletedMin;
++addedMin;
--remaining;
if (remaining == 0) {
++start;
}
}
CFIndex end = 0;
const char* deletedByte = deletedMax - 1;
const char* addedByte = addedMax - 1;
while ((deletedByte >= deletedMin) && (addedByte >= addedMin)) {
if (*deletedByte != *addedByte) {
break;
}
unsigned char byte = *(unsigned char*)deletedByte;
if ((!(byte & (1 << 7))) || TEST_BITS(byte, 0b11000000)) { // 0xxxxxxx or 11xxxxxx indicates a UTF-8 single byte or multi-byte start
++end;
}
--deletedByte;
--addedByte;
}
*deletedRange = CFRangeMake(start, deletedLength - end - start);
XLOG_DEBUG_CHECK(deletedRange->length >= 0);
*addedRange = CFRangeMake(start, addedLength - end - start);
XLOG_DEBUG_CHECK(addedRange->length >= 0);
}
void GIComputeModifiedRanges(NSString* beforeString, NSRange* beforeRange, NSString* afterString, NSRange* afterRange) {
const char* before = beforeString.UTF8String;
const char* after = afterString.UTF8String;
GIComputeHighlightRanges(before, strlen(before), beforeString.length, (CFRange*)beforeRange, after, strlen(after), afterString.length, (CFRange*)afterRange);
}
NSString* GIFormatDateRelativelyFromNow(NSDate* date, BOOL showApproximateTime) {
return GIFormatRelativeDateDifference([NSDate date], date, showApproximateTime);
}
static NSString* _WeekdayName(NSInteger index) {
switch (index) {
case 1: return NSLocalizedString(@"Sunday", nil);
case 2: return NSLocalizedString(@"Monday", nil);
case 3: return NSLocalizedString(@"Tuesday", nil);
case 4: return NSLocalizedString(@"Wednesday", nil);
case 5: return NSLocalizedString(@"Thursday", nil);
case 6: return NSLocalizedString(@"Friday", nil);
case 7: return NSLocalizedString(@"Saturday", nil);
}
XLOG_DEBUG_UNREACHABLE();
return nil;
}
NSString* GIFormatRelativeDateDifference(NSDate* fromDate, NSDate* toDate, BOOL showApproximateTime) {
if (toDate.timeIntervalSinceReferenceDate <= fromDate.timeIntervalSinceReferenceDate) {
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitWeekOfYear | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:fromDate toDate:toDate options:0];
if (components.year == 0) { // Dates are less than 1 year apart
if (components.month == 0) { // Dates are less than 1 month apart
NSDateComponents* fromComponents = [calendar components:(NSCalendarUnitWeekday | NSCalendarUnitWeekOfYear) fromDate:fromDate];
NSDateComponents* toComponents = [calendar components:(NSCalendarUnitWeekday | NSCalendarUnitWeekOfYear | NSCalendarUnitHour) fromDate:toDate];
if (components.weekOfYear == 0) { // Dates are less than 1 week apart
if (components.day == 0) { // Dates are less than 1 day apart
if (components.hour == 0) { // Dates are less than 1 hour apart
if (components.minute >= -1) {
return NSLocalizedString(@"Just now", nil);
}
if (components.minute >= -4) {
return NSLocalizedString(@"Minutes ago", nil);
}
return [NSString stringWithFormat:NSLocalizedString(@"%li minutes ago", nil), 5 * (-components.minute / 5)]; // Rounded to 5 minutes intervals
}
if (components.hour == -1) {
return NSLocalizedString(@"An hour ago", nil);
}
if (components.hour >= -3) {
return [NSString stringWithFormat:NSLocalizedString(@"%li hours ago", nil), -components.hour];
}
// Pass through!
}
if ((toComponents.weekOfYear == fromComponents.weekOfYear) && (toComponents.weekday == fromComponents.weekday)) { // Dates are on the same day
if (showApproximateTime) {
if (toComponents.hour < 12) {
return [NSString stringWithFormat:NSLocalizedString(@"Today, %li AM", nil), toComponents.hour == 0 ? 12 : toComponents.hour];
}
return [NSString stringWithFormat:NSLocalizedString(@"Today, %li PM", nil), toComponents.hour == 12 ? 12 : toComponents.hour - 12];
}
return NSLocalizedString(@"Today", nil);
}
if ((toComponents.weekday == fromComponents.weekday - 1) || ((fromComponents.weekday == 1) && (toComponents.weekday == 7))) { // Dates are on consecutive days
return NSLocalizedString(@"Yesterday", nil);
}
if (toComponents.weekOfYear == fromComponents.weekOfYear) { // Dates are in the same week
return [NSString stringWithFormat:NSLocalizedString(@"This %@", nil), _WeekdayName(toComponents.weekday)];
}
return [NSString stringWithFormat:NSLocalizedString(@"Last %@", nil), _WeekdayName(toComponents.weekday)];
}
if (components.weekOfYear == -1) {
return NSLocalizedString(@"A week ago", nil);
}
return [NSString stringWithFormat:NSLocalizedString(@"%li weeks ago", nil), -components.weekOfYear];
}
if (components.month == -1) {
return NSLocalizedString(@"A month ago", nil);
}
return [NSString stringWithFormat:NSLocalizedString(@"%li months ago", nil), -components.month];
}
if (components.year == -1) {
return NSLocalizedString(@"A year ago", nil);
}
return [NSString stringWithFormat:NSLocalizedString(@"%li years ago", nil), -components.year];
}
return NSLocalizedString(@"Future", nil);
}
void GICGContextAddRoundedRect(CGContextRef context, CGRect rect, CGFloat radius) {
CGContextMoveToPoint(context, rect.origin.x + radius, rect.origin.y);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width - radius, rect.origin.y);
CGContextAddQuadCurveToPoint(context, rect.origin.x + rect.size.width, rect.origin.y, rect.origin.x + rect.size.width, rect.origin.y + radius);
CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - radius);
CGContextAddQuadCurveToPoint(context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x + radius, rect.origin.y + rect.size.height);
CGContextAddQuadCurveToPoint(context, rect.origin.x, rect.origin.y + rect.size.height, rect.origin.x, rect.origin.y + rect.size.height - radius);
CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y + radius);
CGContextAddQuadCurveToPoint(context, rect.origin.x, rect.origin.y, rect.origin.x + radius, rect.origin.y);
}