@@ -182,48 +182,78 @@ - (NSRange)contiguousOrderedListRangeContaining:(NSRange)range
182182 return NSMakeRange (range.location , 0 );
183183 }
184184
185+ NSTextStorage *textStorage = self.host .textView .textStorage ;
186+ NSRange fullRange = NSMakeRange (0 , length);
185187 NSUInteger seedLocation = MIN (range.location , length - 1 );
186- NSRange initialParagraph =
187- [fullText paragraphRangeForRange: NSMakeRange (seedLocation, 0 )];
188- NSRange firstParagraph = initialParagraph;
189188
190- NSInteger precedingCount = 0 ;
189+ NSRange seedRun;
190+ [textStorage attribute: NSParagraphStyleAttributeName
191+ atIndex: seedLocation
192+ longestEffectiveRange: &seedRun
193+ inRange: fullRange];
191194
192- // seek backward over preceding ordered-list paragraphs, counting items
193- while (firstParagraph.location > 0 ) {
194- NSRange previous = [fullText
195- paragraphRangeForRange: NSMakeRange (firstParagraph.location - 1 , 0 )];
196- if (![self detect: NSMakeRange (previous.location, 0 )]) {
195+ NSUInteger firstParagraphStart = seedRun.location ;
196+ NSUInteger lastParagraphEnd = NSMaxRange (seedRun);
197+
198+ // seek backward over preceding ordered-list runs
199+ while (firstParagraphStart > 0 ) {
200+ if (![self detect: NSMakeRange (firstParagraphStart - 1 , 0 )]) {
197201 break ;
198202 }
199- firstParagraph = previous;
200- precedingCount += 1 ;
203+ NSRange previousRun;
204+ [textStorage attribute: NSParagraphStyleAttributeName
205+ atIndex: firstParagraphStart - 1
206+ longestEffectiveRange: &previousRun
207+ inRange: fullRange];
208+ firstParagraphStart = previousRun.location ;
201209 }
202210
203- // seek forward over following ordered-list paragraphs, counting items
204- NSInteger followingCount = 0 ;
205- NSRange lastParagraph = initialParagraph;
206- NSRange cursor = initialParagraph;
207- while (true ) {
208- lastParagraph = cursor;
209- NSUInteger nextLocation = NSMaxRange (cursor);
210- if (nextLocation >= length) {
211- break ;
212- }
213- NSRange next =
214- [fullText paragraphRangeForRange: NSMakeRange (nextLocation, 0 )];
215- if (![self detect: NSMakeRange (next.location, 0 )]) {
211+ // seek forward over following ordered-list runs
212+ while (lastParagraphEnd < length) {
213+ if (![self detect: NSMakeRange (lastParagraphEnd, 0 )]) {
216214 break ;
217215 }
218- cursor = next;
219- followingCount += 1 ;
216+ NSRange nextRun;
217+ [textStorage attribute: NSParagraphStyleAttributeName
218+ atIndex: lastParagraphEnd
219+ longestEffectiveRange: &nextRun
220+ inRange: fullRange];
221+ lastParagraphEnd = NSMaxRange (nextRun);
220222 }
221223
224+ NSRange listRange =
225+ NSMakeRange (firstParagraphStart, lastParagraphEnd - firstParagraphStart);
226+
222227 if (outCount != nullptr ) {
223- *outCount = precedingCount + followingCount + 1 ;
228+ *outCount =
229+ [self countParagraphsInRange: listRange
230+ inText: self .host.textView.textStorage.string];
224231 }
225- return NSMakeRange (firstParagraph.location ,
226- NSMaxRange (lastParagraph) - firstParagraph.location);
232+ return listRange;
233+ }
234+
235+ // counts paragraphs (newline-delimited) within a range that is already known
236+ // to start and end exactly on paragraph boundaries
237+ - (NSInteger )countParagraphsInRange : (NSRange )listRange inText : (NSString *)text {
238+ if (listRange.length == 0 ) {
239+ return 0 ;
240+ }
241+
242+ NSCharacterSet *newlineSet = [NSCharacterSet newlineCharacterSet ];
243+ NSUInteger rangeEnd = NSMaxRange (listRange);
244+ NSUInteger cursor = listRange.location ;
245+ NSInteger count = 0 ;
246+
247+ while (cursor < rangeEnd) {
248+ count += 1 ;
249+ NSRange newline =
250+ [text rangeOfCharacterFromSet: newlineSet
251+ options: 0
252+ range: NSMakeRange (cursor, rangeEnd - cursor)];
253+ cursor = newline.location != NSNotFound ? NSMaxRange (newline) : rangeEnd;
254+ }
255+
256+ return count;
227257}
228258
229259@end
0 commit comments