Skip to content

Commit ee9cc11

Browse files
authored
iOS 0 length range detection revamp (#190)
I found a few really strange issues that resulted from previous assumptions; previously I thought detecting a style at a 0 length range was equivalent to cursor being there. Later code proved me wrong (mainly zero width space logic) and I decided to revamp the way it works. Now: - if cursor is at the place, just use typing attributes to retrieve needed values - otherwise; - if the place is as the very end of the input, either there's no style (inline styles) or check the first character of the paragraph if it exists (paragraph styles) - otherwise check attributes at the given index using apple's API for this Apart from that, this PR also fixes a situation where backspacing a list or blockquote wouldn't remove the style.
1 parent f562a15 commit ee9cc11

12 files changed

Lines changed: 146 additions & 79 deletions

example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2690,7 +2690,7 @@ SPEC CHECKSUMS:
26902690
ReactAppDependencyProvider: 77a7129540c0e06ab6be9a2bcb887d3d2e594431
26912691
ReactCodegen: b62625187ce853918021a7a9178cc406e9820d56
26922692
ReactCommon: d07170f92e0e853091a70b2741b7c43f5dfdea73
2693-
ReactNativeEnriched: b11d66700889cd36c9938a825736de1929349b24
2693+
ReactNativeEnriched: ce7a893fdefce993826a3177da6811b513e26526
26942694
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
26952695
Yoga: 6af5d1e0290903c82b3e0cb6836a5f898c1c4634
26962696

ios/styles/BlockQuoteStyle.mm

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,33 @@ - (void)removeTypingAttributes {
118118
[self removeAttributes:_input->textView.selectedRange];
119119
}
120120

121-
// removing first quote line by backspacing doesn't remove typing attributes because it doesn't run textViewDidChange
122-
// so we try guessing that a point should be deleted here
123121
- (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text {
124-
if([self detectStyle:_input->textView.selectedRange] &&
125-
NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0)) &&
126-
[text isEqualToString:@""]
122+
if(
123+
[self detectStyle:_input->textView.selectedRange] &&
124+
NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0)) &&
125+
[text isEqualToString:@""]
127126
) {
127+
// removing first quote line by backspacing doesn't remove typing attributes because it doesn't run textViewDidChange
128+
// so we try guessing that a line should be deleted here
128129
NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:_input->textView.selectedRange];
129130
[self removeAttributes:paragraphRange];
130131
return YES;
132+
} else if(
133+
[self detectStyle:_input->textView.selectedRange] &&
134+
[text isEqualToString:@""]
135+
) {
136+
// other case; make sure removing all the (non newline) text from a quto line also removes the line itself
137+
NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:range];
138+
NSValue *nonNewlineVal = [ParagraphsUtils getNonNewlineRangesIn:_input->textView range:paragraphRange].firstObject;
139+
if(nonNewlineVal == nullptr) {
140+
return NO;
141+
}
142+
NSRange nonNewlineRange = [nonNewlineVal rangeValue];
143+
if(NSEqualRanges(range, nonNewlineRange)) {
144+
[self removeAttributes:range];
145+
[TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:_input withSelection:YES];
146+
return YES;
147+
}
131148
}
132149
return NO;
133150
}
@@ -145,22 +162,11 @@ - (BOOL)detectStyle:(NSRange)range {
145162
}
146163
];
147164
} else {
148-
NSInteger searchLocation = range.location;
149-
if(searchLocation == _input->textView.textStorage.length) {
150-
NSParagraphStyle *pStyle = _input->textView.typingAttributes[NSParagraphStyleAttributeName];
151-
return [self styleCondition:pStyle :NSMakeRange(0, 0)];
152-
}
153-
154-
NSRange paragraphRange = NSMakeRange(0, 0);
155-
NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
156-
NSParagraphStyle *paragraph = [_input->textView.textStorage
157-
attribute:NSParagraphStyleAttributeName
158-
atIndex:searchLocation
159-
longestEffectiveRange: &paragraphRange
160-
inRange:inputRange
165+
return [OccurenceUtils detect:NSParagraphStyleAttributeName withInput:_input atIndex:range.location checkPrevious:YES
166+
withCondition:^BOOL(id _Nullable value, NSRange range) {
167+
return [self styleCondition:value :range];
168+
}
161169
];
162-
163-
return [self styleCondition:paragraph :NSMakeRange(0, 0)];
164170
}
165171
}
166172

ios/styles/BoldStyle.mm

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,11 @@ - (BOOL)detectStyle:(NSRange)range {
9898
}
9999
];
100100
} else {
101-
UIFont *currentFontAttr = (UIFont *)_input->textView.typingAttributes[NSFontAttributeName];
102-
return [self styleCondition:currentFontAttr :range];
101+
return [OccurenceUtils detect:NSFontAttributeName withInput:_input atIndex:range.location checkPrevious:NO
102+
withCondition:^BOOL(id _Nullable value, NSRange range) {
103+
return [self styleCondition:value :range];
104+
}
105+
];
103106
}
104107
}
105108

ios/styles/HeadingStyleBase.mm

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,11 @@ - (BOOL)detectStyle:(NSRange)range {
117117
}
118118
];
119119
} else {
120-
UIFont *currentFontAttr = (UIFont *)[self typedInput]->textView.typingAttributes[NSFontAttributeName];
121-
if(currentFontAttr == nullptr) {
122-
return false;
123-
}
124-
return currentFontAttr.pointSize == [self getHeadingFontSize];
120+
return [OccurenceUtils detect:NSFontAttributeName withInput:[self typedInput] atIndex:range.location checkPrevious:YES
121+
withCondition:^BOOL(id _Nullable value, NSRange range) {
122+
return [self styleCondition:value :range];
123+
}
124+
];
125125
}
126126
}
127127

ios/styles/InlineCodeStyle.mm

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,11 @@ - (BOOL)detectStyle:(NSRange)range {
140140

141141
return detected;
142142
} else {
143-
UIColor *currentBgColorAttr = (UIColor *)_input->textView.typingAttributes[NSBackgroundColorAttributeName];
144-
return [self styleCondition:currentBgColorAttr :range];
143+
return [OccurenceUtils detect:NSBackgroundColorAttributeName withInput:_input atIndex:range.location checkPrevious:NO
144+
withCondition:^BOOL(id _Nullable value, NSRange range) {
145+
return [self styleCondition:value :range];
146+
}
147+
];
145148
}
146149
}
147150

ios/styles/ItalicStyle.mm

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ - (BOOL)detectStyle:(NSRange)range {
8383
}
8484
];
8585
} else {
86-
UIFont *currentFontAttr = (UIFont *)_input->textView.typingAttributes[NSFontAttributeName];
87-
if(currentFontAttr == nullptr) {
88-
return false;
89-
}
90-
return [currentFontAttr isItalic];
86+
return [OccurenceUtils detect:NSFontAttributeName withInput:_input atIndex:range.location checkPrevious:NO
87+
withCondition:^BOOL(id _Nullable value, NSRange range) {
88+
return [self styleCondition:value :range];
89+
}
90+
];
9191
}
9292
}
9393

ios/styles/OrderedListStyle.mm

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,33 @@ - (void)removeTypingAttributes {
130130
[self removeAttributes:_input->textView.selectedRange];
131131
}
132132

133-
// removing first list point by backspacing doesn't remove typing attributes because it doesn't run textViewDidChange
134-
// so we try guessing that a point should be deleted here
135133
- (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text {
136-
if([self detectStyle:_input->textView.selectedRange] &&
137-
NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0)) &&
138-
[text isEqualToString:@""]
134+
if(
135+
[self detectStyle:_input->textView.selectedRange] &&
136+
NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0)) &&
137+
[text isEqualToString:@""]
139138
) {
139+
// removing first list point by backspacing doesn't remove typing attributes because it doesn't run textViewDidChange
140+
// so we try guessing that a point should be deleted here
140141
NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:_input->textView.selectedRange];
141142
[self removeAttributes:paragraphRange];
142143
return YES;
144+
} else if(
145+
[self detectStyle:_input->textView.selectedRange] &&
146+
[text isEqualToString:@""]
147+
) {
148+
// other case; make sure removing all the (non newline) text from a list item also removes the item itself
149+
NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:range];
150+
NSValue *nonNewlineVal = [ParagraphsUtils getNonNewlineRangesIn:_input->textView range:paragraphRange].firstObject;
151+
if(nonNewlineVal == nullptr) {
152+
return NO;
153+
}
154+
NSRange nonNewlineRange = [nonNewlineVal rangeValue];
155+
if(NSEqualRanges(range, nonNewlineRange)) {
156+
[self removeAttributes:range];
157+
[TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:_input withSelection:YES];
158+
return YES;
159+
}
143160
}
144161
return NO;
145162
}
@@ -187,22 +204,11 @@ - (BOOL)detectStyle:(NSRange)range {
187204
}
188205
];
189206
} else {
190-
NSInteger searchLocation = range.location;
191-
if(searchLocation == _input->textView.textStorage.length) {
192-
NSParagraphStyle *pStyle = _input->textView.typingAttributes[NSParagraphStyleAttributeName];
193-
return [self styleCondition:pStyle :NSMakeRange(0, 0)];
194-
}
195-
196-
NSRange paragraphRange = NSMakeRange(0, 0);
197-
NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
198-
NSParagraphStyle *paragraph = [_input->textView.textStorage
199-
attribute:NSParagraphStyleAttributeName
200-
atIndex:searchLocation
201-
longestEffectiveRange: &paragraphRange
202-
inRange:inputRange
207+
return [OccurenceUtils detect:NSParagraphStyleAttributeName withInput:_input atIndex:range.location checkPrevious:YES
208+
withCondition:^BOOL(id _Nullable value, NSRange range) {
209+
return [self styleCondition:value :range];
210+
}
203211
];
204-
205-
return [self styleCondition:paragraph :NSMakeRange(0, 0)];
206212
}
207213
}
208214

ios/styles/StrikethroughStyle.mm

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ - (BOOL)detectStyle:(NSRange)range {
5656
}
5757
];
5858
} else {
59-
NSNumber *currenStrikethroughAttr = (NSNumber *)_input->textView.typingAttributes[NSStrikethroughStyleAttributeName];
60-
return currenStrikethroughAttr != nullptr;
59+
return [OccurenceUtils detect:NSStrikethroughStyleAttributeName withInput:_input atIndex:range.location checkPrevious:NO
60+
withCondition:^BOOL(id _Nullable value, NSRange range) {
61+
return [self styleCondition:value :range];
62+
}
63+
];
6164
}
6265
}
6366

ios/styles/UnderlineStyle.mm

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,11 @@ - (BOOL)detectStyle:(NSRange)range {
8888
}
8989
];
9090
} else {
91-
NSNumber *currentUnderlineAttr = (NSNumber *)_input->textView.typingAttributes[NSUnderlineStyleAttributeName];
92-
return [self styleCondition:currentUnderlineAttr :range];
91+
return [OccurenceUtils detect:NSUnderlineStyleAttributeName withInput:_input atIndex:range.location checkPrevious:NO
92+
withCondition:^BOOL(id _Nullable value, NSRange range) {
93+
return [self styleCondition:value :range];
94+
}
95+
];
9396
}
9497
}
9598

ios/styles/UnorderedListStyle.mm

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,33 @@ - (void)removeTypingAttributes {
130130
[self removeAttributes:_input->textView.selectedRange];
131131
}
132132

133-
// removing first list point by backspacing doesn't remove typing attributes because it doesn't run textViewDidChange
134-
// so we try guessing that a point should be deleted here
135133
- (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text {
136-
if([self detectStyle:_input->textView.selectedRange] &&
137-
NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0)) &&
138-
[text isEqualToString:@""]
134+
if(
135+
[self detectStyle:_input->textView.selectedRange] &&
136+
NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0)) &&
137+
[text isEqualToString:@""]
139138
) {
139+
// removing first list point by backspacing doesn't remove typing attributes because it doesn't run textViewDidChange
140+
// so we try guessing that a point should be deleted here
140141
NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:_input->textView.selectedRange];
141142
[self removeAttributes:paragraphRange];
142143
return YES;
144+
} else if(
145+
[self detectStyle:_input->textView.selectedRange] &&
146+
[text isEqualToString:@""]
147+
) {
148+
// other case; make sure removing all the (non newline) text from a list item also removes the item itself
149+
NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:range];
150+
NSValue *nonNewlineVal = [ParagraphsUtils getNonNewlineRangesIn:_input->textView range:paragraphRange].firstObject;
151+
if(nonNewlineVal == nullptr) {
152+
return NO;
153+
}
154+
NSRange nonNewlineRange = [nonNewlineVal rangeValue];
155+
if(NSEqualRanges(range, nonNewlineRange)) {
156+
[self removeAttributes:range];
157+
[TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:_input withSelection:YES];
158+
return YES;
159+
}
143160
}
144161
return NO;
145162
}
@@ -187,22 +204,11 @@ - (BOOL)detectStyle:(NSRange)range {
187204
}
188205
];
189206
} else {
190-
NSInteger searchLocation = range.location;
191-
if(searchLocation == _input->textView.textStorage.length) {
192-
NSParagraphStyle *pStyle = _input->textView.typingAttributes[NSParagraphStyleAttributeName];
193-
return [self styleCondition:pStyle :NSMakeRange(0, 0)];
194-
}
195-
196-
NSRange paragraphRange = NSMakeRange(0, 0);
197-
NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
198-
NSParagraphStyle *paragraph = [_input->textView.textStorage
199-
attribute:NSParagraphStyleAttributeName
200-
atIndex:searchLocation
201-
longestEffectiveRange: &paragraphRange
202-
inRange:inputRange
207+
return [OccurenceUtils detect:NSParagraphStyleAttributeName withInput:_input atIndex:range.location checkPrevious:YES
208+
withCondition:^BOOL(id _Nullable value, NSRange range) {
209+
return [self styleCondition:value :range];
210+
}
203211
];
204-
205-
return [self styleCondition:paragraph :NSMakeRange(0, 0)];
206212
}
207213
}
208214

0 commit comments

Comments
 (0)