11#import " EnrichedTextInputView.h"
2+ #import " OrderedListMetricsUtils.h"
23#import " RangeUtils.h"
34#import " StyleHeaders.h"
45#import " StyleUtils.h"
56#import " TextInsertionUtils.h"
67
7- @implementation OrderedListStyle {
8- // we don't want to re-measure each marker's actual
9- // width. We estimate the width with cached metrics instead
10- UIFont *_cachedMarkerFont;
11- CGFloat _cachedDigitWidth;
12- CGFloat _cachedDotWidth;
13- }
8+ @implementation OrderedListStyle
149
1510+ (StyleType)getType {
1611 return OrderedList;
@@ -37,8 +32,10 @@ - (void)applyStyling:(NSRange)range {
3732 // re-indent all of it - even when only a single paragraph is dirty (e.g. an
3833 // item was just added)
3934 NSInteger itemCount = 0 ;
40- NSRange listRange = [self contiguousOrderedListRangeContaining: range
41- itemCount: &itemCount];
35+ NSRange listRange =
36+ [OrderedListMetricsUtils contiguousOrderedListRangeContaining: range
37+ forStyle: self
38+ itemCount: &itemCount];
4239
4340 [self applyIndentForListRange: listRange itemCount: itemCount];
4441}
@@ -85,52 +82,20 @@ - (void)recalculateListsAroundEditedRange:(NSRange)range {
8582 }
8683
8784 NSInteger itemCount = 0 ;
88- NSRange listRange =
89- [self contiguousOrderedListRangeContaining: NSMakeRange (seed, 0 )
90- itemCount: &itemCount];
85+ NSRange listRange = [OrderedListMetricsUtils
86+ contiguousOrderedListRangeContaining: NSMakeRange (seed, 0 )
87+ forStyle: self
88+ itemCount: &itemCount];
9189 [handled addObject: [NSValue valueWithRange: listRange]];
9290 [self applyIndentForListRange: listRange itemCount: itemCount];
9391 }
9492}
9593
96- - (void )ensureMarkerMetricsForFont : (UIFont *)font {
97- if (_cachedMarkerFont != nil && [_cachedMarkerFont isEqual: font]) {
98- return ;
99- }
100- _cachedMarkerFont = font;
101- NSDictionary *attrs = @{NSFontAttributeName : font};
102- _cachedDigitWidth = [@" 0" sizeWithAttributes: attrs].width ;
103- _cachedDotWidth = [@" ." sizeWithAttributes: attrs].width ;
104- }
105-
106- - (NSInteger )digitCountOf : (NSInteger )n {
107- NSInteger count = 1 ;
108- NSInteger value = MAX (n, 1 );
109- while (value >= 10 ) {
110- value /= 10 ;
111- count += 1 ;
112- }
113- return count;
114- }
115-
116- // computes the shared marker-column indent for a list of the given item
117- // count. The largest marker value equals the item count (numbering starts
118- // at 1); if its width overflows the configured margin we expand to fit it
119- - (CGFloat)headIndentForItemCount : (NSInteger )itemCount {
120- UIFont *markerFont = [self .host.config orderedListMarkerFont ];
121- [self ensureMarkerMetricsForFont: markerFont];
122-
123- NSInteger digitCount = [self digitCountOf: itemCount];
124- CGFloat widestMarkerWidth = digitCount * _cachedDigitWidth + _cachedDotWidth;
125-
126- CGFloat markerColumnWidth =
127- MAX ([self .host.config orderedListMarginLeft ], widestMarkerWidth);
128- return markerColumnWidth + [self .host.config orderedListGapWidth ];
129- }
130-
13194- (void )applyIndentForListRange : (NSRange )listRange
13295 itemCount : (NSInteger )itemCount {
133- CGFloat listHeadIndent = [self headIndentForItemCount: itemCount];
96+ CGFloat listHeadIndent =
97+ [OrderedListMetricsUtils headIndentForItemCount: itemCount
98+ config: self .host.config];
13499
135100 [self .host.textView.textStorage
136101 enumerateAttribute: NSParagraphStyleAttributeName
@@ -185,99 +150,14 @@ - (void)applyStylingToTypingAttrs:(NSMutableDictionary *)attributes {
185150 pStyle.headIndent = existingStyle.headIndent ;
186151 pStyle.firstLineHeadIndent = existingStyle.firstLineHeadIndent ;
187152 } else {
188- CGFloat fallbackIndent = [self headIndentForItemCount: 1 ];
153+ CGFloat fallbackIndent =
154+ [OrderedListMetricsUtils headIndentForItemCount: 1
155+ config: self .host.config];
189156 pStyle.headIndent = fallbackIndent;
190157 pStyle.firstLineHeadIndent = fallbackIndent;
191158 }
192159
193160 attributes[NSParagraphStyleAttributeName ] = pStyle;
194161}
195162
196- // walks paragraphs backward and forward from the given range to find the full
197- // contiguous run of ordered-list items it belongs to, and counts them
198- - (NSRange )contiguousOrderedListRangeContaining : (NSRange )range
199- itemCount : (NSInteger *)outCount {
200- NSString *fullText = self.host .textView .textStorage .string ;
201- NSUInteger length = fullText.length ;
202- if (length == 0 ) {
203- if (outCount != nullptr ) {
204- *outCount = 0 ;
205- }
206- return NSMakeRange (range.location , 0 );
207- }
208-
209- NSTextStorage *textStorage = self.host .textView .textStorage ;
210- NSRange fullRange = NSMakeRange (0 , length);
211- NSUInteger seedLocation = MIN (range.location , length - 1 );
212-
213- NSRange seedRun;
214- [textStorage attribute: NSParagraphStyleAttributeName
215- atIndex: seedLocation
216- longestEffectiveRange: &seedRun
217- inRange: fullRange];
218-
219- NSUInteger firstParagraphStart = seedRun.location ;
220- NSUInteger lastParagraphEnd = NSMaxRange (seedRun);
221-
222- // seek backward over preceding ordered-list runs
223- while (firstParagraphStart > 0 ) {
224- if (![self detect: NSMakeRange (firstParagraphStart - 1 , 0 )]) {
225- break ;
226- }
227- NSRange previousRun;
228- [textStorage attribute: NSParagraphStyleAttributeName
229- atIndex: firstParagraphStart - 1
230- longestEffectiveRange: &previousRun
231- inRange: fullRange];
232- firstParagraphStart = previousRun.location ;
233- }
234-
235- // seek forward over following ordered-list runs
236- while (lastParagraphEnd < length) {
237- if (![self detect: NSMakeRange (lastParagraphEnd, 0 )]) {
238- break ;
239- }
240- NSRange nextRun;
241- [textStorage attribute: NSParagraphStyleAttributeName
242- atIndex: lastParagraphEnd
243- longestEffectiveRange: &nextRun
244- inRange: fullRange];
245- lastParagraphEnd = NSMaxRange (nextRun);
246- }
247-
248- NSRange listRange =
249- NSMakeRange (firstParagraphStart, lastParagraphEnd - firstParagraphStart);
250-
251- if (outCount != nullptr ) {
252- *outCount =
253- [self countParagraphsInRange: listRange
254- inText: self .host.textView.textStorage.string];
255- }
256- return listRange;
257- }
258-
259- // counts paragraphs (newline-delimited) within a range that is already known
260- // to start and end exactly on paragraph boundaries
261- - (NSInteger )countParagraphsInRange : (NSRange )listRange inText : (NSString *)text {
262- if (listRange.length == 0 ) {
263- return 0 ;
264- }
265-
266- NSCharacterSet *newlineSet = [NSCharacterSet newlineCharacterSet ];
267- NSUInteger rangeEnd = NSMaxRange (listRange);
268- NSUInteger cursor = listRange.location ;
269- NSInteger count = 0 ;
270-
271- while (cursor < rangeEnd) {
272- count += 1 ;
273- NSRange newline =
274- [text rangeOfCharacterFromSet: newlineSet
275- options: 0
276- range: NSMakeRange (cursor, rangeEnd - cursor)];
277- cursor = newline.location != NSNotFound ? NSMaxRange (newline) : rangeEnd;
278- }
279-
280- return count;
281- }
282-
283163@end
0 commit comments