@@ -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