Skip to content

Commit 8ad26ca

Browse files
fix: merge contiguous code blocks
1 parent 7d65196 commit 8ad26ca

1 file changed

Lines changed: 117 additions & 67 deletions

File tree

ios/utils/LayoutManagerExtension.mm

Lines changed: 117 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,122 @@ - (void)my_drawBackgroundForGlyphRange:(NSRange)glyphRange atPoint:(CGPoint)orig
5353
EnrichedTextInputView *typedInput = (EnrichedTextInputView *)self.input;
5454
if(typedInput == nullptr) { return; }
5555

56+
NSRange inputRange = NSMakeRange(0, typedInput->textView.textStorage.length);
57+
58+
[self drawBlockQuotes:typedInput origin:origin inputRange:inputRange];
59+
[self drawLists:typedInput origin:origin inputRange:inputRange];
60+
[self drawCodeBlocks:typedInput origin:origin inputRange:inputRange];
61+
}
62+
63+
- (void)drawCodeBlocks:(EnrichedTextInputView *)typedInput origin:(CGPoint)origin inputRange:(NSRange)inputRange
64+
{
65+
CodeBlockStyle *codeBlockStyle = typedInput->stylesDict[@([CodeBlockStyle getStyleType])];
66+
if(codeBlockStyle == nullptr) { return; }
67+
68+
NSArray<StylePair *> *allCodeBlocks = [codeBlockStyle findAllOccurences:inputRange];
69+
NSArray<StylePair *> *mergedCodeBlocks = [self mergeContiguousStylePairs:allCodeBlocks];
70+
UIColor *bgColor = [[typedInput->config codeBlockBgColor] colorWithAlphaIfNotTransparent:0.4];
71+
CGFloat radius = [typedInput->config codeBlockBorderRadius];
72+
[bgColor setFill];
73+
74+
for (StylePair *pair in mergedCodeBlocks) {
75+
NSRange blockCharacterRange = [pair.rangeValue rangeValue];
76+
if (blockCharacterRange.length == 0) continue;
77+
78+
NSArray *paragraphs = [ParagraphsUtils getSeparateParagraphsRangesIn:typedInput->textView range:blockCharacterRange];
79+
if (paragraphs.count == 0) continue;
80+
81+
NSRange firstParagraphRange = [((NSValue *)[paragraphs firstObject]) rangeValue];
82+
NSRange lastParagraphRange = [((NSValue *)[paragraphs lastObject]) rangeValue];
83+
84+
for (NSValue *paragraphValue in paragraphs) {
85+
NSRange paragraphCharacterRange = [paragraphValue rangeValue];
86+
87+
BOOL isFirstParagraph = NSEqualRanges(paragraphCharacterRange, firstParagraphRange);
88+
BOOL isLastParagraph = NSEqualRanges(paragraphCharacterRange, lastParagraphRange);
89+
90+
NSRange paragraphGlyphRange = [self glyphRangeForCharacterRange:paragraphCharacterRange actualCharacterRange:NULL];
91+
92+
__block BOOL isFirstLineOfParagraph = YES;
93+
94+
[self enumerateLineFragmentsForGlyphRange:paragraphGlyphRange
95+
usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) {
96+
97+
CGRect lineBgRect = rect;
98+
lineBgRect.origin.x = origin.x;
99+
lineBgRect.origin.y += origin.y;
100+
lineBgRect.size.width = textContainer.size.width;
101+
102+
UIRectCorner cornersForThisLine = 0;
103+
104+
if (isFirstParagraph && isFirstLineOfParagraph) {
105+
cornersForThisLine = UIRectCornerTopLeft | UIRectCornerTopRight;
106+
}
107+
108+
BOOL isLastLineOfParagraph = (NSMaxRange(glyphRange) >= NSMaxRange(paragraphGlyphRange));
109+
110+
if (isLastParagraph && isLastLineOfParagraph) {
111+
cornersForThisLine = cornersForThisLine | UIRectCornerBottomLeft | UIRectCornerBottomRight;
112+
}
113+
114+
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:lineBgRect
115+
byRoundingCorners:cornersForThisLine
116+
cornerRadii:CGSizeMake(radius, radius)];
117+
[path fill];
118+
119+
isFirstLineOfParagraph = NO;
120+
}];
121+
}
122+
}
123+
}
124+
125+
- (NSArray<StylePair *> *)mergeContiguousStylePairs:(NSArray<StylePair *> *)pairs
126+
{
127+
if (pairs.count == 0) {
128+
return @[];
129+
}
130+
131+
NSMutableArray<StylePair *> *mergedPairs = [[NSMutableArray alloc] init];
132+
StylePair *currentPair = pairs[0];
133+
NSRange currentRange = [currentPair.rangeValue rangeValue];
134+
for (NSUInteger i = 1; i < pairs.count; i++) {
135+
StylePair *nextPair = pairs[i];
136+
NSRange nextRange = [nextPair.rangeValue rangeValue];
137+
138+
// The Gap Check:
139+
// NSMaxRange(currentRange) is where the current block ends.
140+
// nextRange.location is where the next block starts.
141+
if (NSMaxRange(currentRange) == nextRange.location) {
142+
// They touch perfectly (no gap). Merge them.
143+
currentRange.length += nextRange.length;
144+
} else {
145+
// There is a gap (indices don't match).
146+
// 1. Save the finished block.
147+
StylePair *mergedPair = [[StylePair alloc] init];
148+
mergedPair.rangeValue = [NSValue valueWithRange:currentRange];
149+
mergedPair.styleValue = currentPair.styleValue;
150+
[mergedPairs addObject:mergedPair];
151+
152+
// 2. Start a brand new block.
153+
currentPair = nextPair;
154+
currentRange = nextRange;
155+
}
156+
}
157+
158+
// Add the final block
159+
StylePair *lastPair = [[StylePair alloc] init];
160+
lastPair.rangeValue = [NSValue valueWithRange:currentRange];
161+
lastPair.styleValue = currentPair.styleValue;
162+
[mergedPairs addObject:lastPair];
163+
164+
return mergedPairs;
165+
}
166+
167+
- (void)drawBlockQuotes:(EnrichedTextInputView *)typedInput origin:(CGPoint)origin inputRange:(NSRange)inputRange
168+
{
56169
BlockQuoteStyle *bqStyle = typedInput->stylesDict[@([BlockQuoteStyle getStyleType])];
57170
if(bqStyle == nullptr) { return; }
58171

59-
NSRange inputRange = NSMakeRange(0, typedInput->textView.textStorage.length);
60-
61172
// it isn't the most performant but we have to check for all the blockquotes each time and redraw them
62173
NSArray *allBlockquotes = [bqStyle findAllOccurences:inputRange];
63174

@@ -79,7 +190,10 @@ - (void)my_drawBackgroundForGlyphRange:(NSRange)glyphRange atPoint:(CGPoint)orig
79190
}
80191
];
81192
}
82-
193+
}
194+
195+
- (void)drawLists:(EnrichedTextInputView *)typedInput origin:(CGPoint)origin inputRange:(NSRange)inputRange
196+
{
83197
UnorderedListStyle *ulStyle = typedInput->stylesDict[@([UnorderedListStyle getStyleType])];
84198
OrderedListStyle *olStyle = typedInput->stylesDict[@([OrderedListStyle getStyleType])];
85199
if(ulStyle == nullptr || olStyle == nullptr) { return; }
@@ -131,70 +245,6 @@ - (void)my_drawBackgroundForGlyphRange:(NSRange)glyphRange atPoint:(CGPoint)orig
131245
];
132246
}
133247
}
134-
135-
[self drawCodeBlocks:typedInput origin:origin];
136-
}
137-
138-
- (void)drawCodeBlocks:(EnrichedTextInputView *)typedInput origin:(CGPoint)origin
139-
{
140-
NSRange inputRange = NSMakeRange(0, typedInput->textView.textStorage.length);
141-
CodeBlockStyle *codeBlockStyle = typedInput->stylesDict[@([CodeBlockStyle getStyleType])];
142-
if(codeBlockStyle == nullptr) { return; }
143-
144-
NSArray<StylePair *> *allCodeBlocks = [codeBlockStyle findAllOccurences:inputRange];
145-
UIColor *bgColor = [[typedInput->config codeBlockBgColor] colorWithAlphaIfNotTransparent:0.4];
146-
CGFloat radius = [typedInput->config codeBlockBorderRadius];
147-
[bgColor setFill];
148-
149-
for (StylePair *pair in allCodeBlocks) {
150-
NSRange blockCharacterRange = [pair.rangeValue rangeValue];
151-
if (blockCharacterRange.length == 0) continue;
152-
153-
NSArray *paragraphs = [ParagraphsUtils getSeparateParagraphsRangesIn:typedInput->textView range:blockCharacterRange];
154-
if (paragraphs.count == 0) continue;
155-
156-
NSRange firstParagraphRange = [((NSValue *)[paragraphs firstObject]) rangeValue];
157-
NSRange lastParagraphRange = [((NSValue *)[paragraphs lastObject]) rangeValue];
158-
159-
for (NSValue *paragraphValue in paragraphs) {
160-
NSRange paragraphCharacterRange = [paragraphValue rangeValue];
161-
162-
BOOL isFirstParagraph = NSEqualRanges(paragraphCharacterRange, firstParagraphRange);
163-
BOOL isLastParagraph = NSEqualRanges(paragraphCharacterRange, lastParagraphRange);
164-
165-
NSRange paragraphGlyphRange = [self glyphRangeForCharacterRange:paragraphCharacterRange actualCharacterRange:NULL];
166-
167-
__block BOOL isFirstLineOfParagraph = YES;
168-
169-
[self enumerateLineFragmentsForGlyphRange:paragraphGlyphRange
170-
usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) {
171-
172-
CGRect lineBgRect = rect;
173-
lineBgRect.origin.x = origin.x;
174-
lineBgRect.origin.y += origin.y;
175-
lineBgRect.size.width = textContainer.size.width;
176-
177-
UIRectCorner cornersForThisLine = 0;
178-
179-
if (isFirstParagraph && isFirstLineOfParagraph) {
180-
cornersForThisLine = UIRectCornerTopLeft | UIRectCornerTopRight;
181-
}
182-
183-
BOOL isLastLineOfParagraph = (NSMaxRange(glyphRange) >= NSMaxRange(paragraphGlyphRange));
184-
185-
if (isLastParagraph && isLastLineOfParagraph) {
186-
cornersForThisLine = cornersForThisLine | UIRectCornerBottomLeft | UIRectCornerBottomRight;
187-
}
188-
189-
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:lineBgRect
190-
byRoundingCorners:cornersForThisLine
191-
cornerRadii:CGSizeMake(radius, radius)];
192-
[path fill];
193-
194-
isFirstLineOfParagraph = NO;
195-
}];
196-
}
197-
}
198248
}
199249

200250
- (NSString *)markerForList:(NSTextList *)list charIndex:(NSUInteger)index input:(EnrichedTextInputView *)input {

0 commit comments

Comments
 (0)