Skip to content

Commit 3652095

Browse files
fix: handling of zero-width space (ZWS) offset inside parser (#500)
# Summary Fixes: #419 This PR fixes handling of zero-width space (ZWS) offset inside empty paragraphs, ensuring parsed styles are applied in correct position in `applyProcessedStyles`. ## Test Plan 1. Run test case from the issue: #419 2. Insert some other `html` examples by "Set Input's value' button in example app and check if they are displayed properly ## Screenshots / Videos https://github.com/user-attachments/assets/6c1aaad4-6b02-45b8-8422-3039b14ac6a3 ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | Android | ❌ | ## Checklist - [x] E2E tests are passing - [ ] Required E2E tests have been added (if applicable)
1 parent 0dbb3d1 commit 3652095

1 file changed

Lines changed: 34 additions & 18 deletions

File tree

ios/inputParser/InputParser.mm

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -677,22 +677,34 @@ - (void)insertFromHtml:(NSString *_Nonnull)html location:(NSInteger)location {
677677
- (void)applyProcessedStyles:(NSArray *)processedStyles
678678
offsetFromBeginning:(NSInteger)offset
679679
plainTextLength:(NSUInteger)plainTextLength {
680+
// Some paragraph styles (codeblock, blockquote, etc.) insert \u200B
681+
// into empty lines, mutating NSTextStorage length. We need to
682+
// shift subsequent ranges by this offset.
683+
NSInteger zeroWidthSpaceOffset = 0;
684+
680685
for (NSArray *arr in processedStyles) {
681686
// unwrap all info from processed style
682687
NSNumber *styleType = (NSNumber *)arr[0];
683688
StylePair *stylePair = (StylePair *)arr[1];
684689
StyleBase *baseStyle = _input->stylesDict[styleType];
685-
// range must be taking offest into consideration because processed styles'
686-
// ranges are relative to only the new text while we need absolute ranges
687-
// relative to the whole existing text
690+
NSRange parsedRange = [stylePair.rangeValue rangeValue];
691+
NSUInteger textLengthBeforeStyleApplied =
692+
_input->textView.textStorage.string.length;
693+
// range must be taking zeroWidthSpaceOffset and offest into consideration
694+
// because processed styles ranges are relative to only the new text while
695+
// we need absolute ranges relative to the whole existing text
688696
NSRange styleRange =
689-
NSMakeRange(offset + [stylePair.rangeValue rangeValue].location,
690-
[stylePair.rangeValue rangeValue].length);
697+
NSMakeRange(offset + zeroWidthSpaceOffset + parsedRange.location,
698+
parsedRange.length);
691699

692700
// of course any changes here need to take blocks and conflicts into
693701
// consideration
694702
if ([_input handleStyleBlocksAndConflicts:[[baseStyle class] getType]
695703
range:styleRange]) {
704+
BOOL shouldAddTypingAttr =
705+
styleRange.location + styleRange.length ==
706+
plainTextLength + offset + zeroWidthSpaceOffset;
707+
696708
if ([styleType isEqualToNumber:@([LinkStyle getType])]) {
697709
LinkData *linkData = (LinkData *)stylePair.styleValue;
698710
[((LinkStyle *)baseStyle) addLink:linkData
@@ -713,32 +725,36 @@ - (void)applyProcessedStyles:(NSArray *)processedStyles
713725

714726
// First apply the checkbox list style to the entire range with
715727
// unchecked value
716-
BOOL shouldAddTypingAttr =
717-
styleRange.location + styleRange.length == plainTextLength;
718728
[cbLStyle addWithChecked:NO
719729
range:styleRange
720730
withTyping:shouldAddTypingAttr
721731
withDirtyRange:YES];
722732

723-
if (!checkboxStates && checkboxStates.count == 0) {
724-
continue;
725-
}
726-
// Then toggle checked checkboxes
727-
for (NSNumber *key in checkboxStates) {
728-
NSUInteger checkboxPosition = offset + [key unsignedIntegerValue];
729-
BOOL isChecked = [checkboxStates[key] boolValue];
730-
if (isChecked) {
731-
[cbLStyle toggleCheckedAt:checkboxPosition];
733+
if (checkboxStates && checkboxStates.count > 0) {
734+
// Then toggle checked checkboxes
735+
for (NSNumber *key in checkboxStates) {
736+
NSUInteger checkboxPosition =
737+
offset + zeroWidthSpaceOffset + [key unsignedIntegerValue];
738+
BOOL isChecked = [checkboxStates[key] boolValue];
739+
if (isChecked) {
740+
[cbLStyle toggleCheckedAt:checkboxPosition];
741+
}
732742
}
733743
}
734744
} else {
735-
BOOL shouldAddTypingAttr =
736-
styleRange.location + styleRange.length == plainTextLength;
737745
[baseStyle add:styleRange
738746
withTyping:shouldAddTypingAttr
739747
withDirtyRange:YES];
740748
}
741749
}
750+
751+
NSInteger delta = (NSInteger)_input->textView.textStorage.string.length -
752+
(NSInteger)textLengthBeforeStyleApplied;
753+
// Image shifts are already handled by _precedingImageCount during tag
754+
// finalization.
755+
if (delta != 0 && ![styleType isEqualToNumber:@([ImageStyle getType])]) {
756+
zeroWidthSpaceOffset += delta;
757+
}
742758
}
743759
[_input anyTextMayHaveBeenModified];
744760
}

0 commit comments

Comments
 (0)