Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Prevent occasional NSString substring out of bounds exception.

  • Loading branch information...
commit d2a5142735196984a08cd9307d78b73f27f85e23 1 parent 8d2232a
@mugginsoft authored
Showing with 129 additions and 124 deletions.
  1. +129 −124 SMLLineNumbers.m
View
253 SMLLineNumbers.m
@@ -129,134 +129,139 @@ - (void)updateLineNumbersForClipView:(NSClipView *)clipView checkWidth:(BOOL)che
}
self.updatingLineNumbersForClipView = clipView;
- SMLTextView *textView = [clipView documentView];
-
- if ([[document valueForKey:MGSFOShowLineNumberGutter] boolValue] == NO || textView == nil) {
- if (checkWidth == YES && recolour == YES) {
- [[document valueForKey:ro_MGSFOSyntaxColouring] pageRecolourTextView:textView];
- }
- goto allDone;
- }
-
- NSScrollView *scrollView = (NSScrollView *)[clipView superview];
- addToScrollPoint = 0;
- if (scrollView == [document valueForKey:ro_MGSFOScrollView]) {
- gutterScrollView = [document valueForKey:ro_MGSFOGutterScrollView];
- } else {
- goto allDone;
- }
-
- // get break points from delegate
- NSSet* breakpoints = NULL;
- id breakpointDelegate = [[MGSFragaria currentInstance] objectForKey:MGSFOBreakpointDelegate];
- if (breakpointDelegate && [breakpointDelegate respondsToSelector:@selector(breakpointsForFile:)])
- {
- breakpoints = [breakpointDelegate breakpointsForFile:[gutterScrollView.documentView fileName]];
- }
-
- NSLayoutManager *layoutManager = [textView layoutManager];
- NSRect visibleRect = [[scrollView contentView] documentVisibleRect];
- NSRange visibleRange = [layoutManager glyphRangeForBoundingRect:visibleRect inTextContainer:[textView textContainer]];
- NSString *textString = [textView string];
- NSString *searchString = [textString substringWithRange:NSMakeRange(0,visibleRange.location)];
-
- for (idx = 0, lineNumber = 0; idx < (NSInteger)visibleRange.location; lineNumber++) {
- idx = NSMaxRange([searchString lineRangeForRange:NSMakeRange(idx, 0)]);
- }
-
- NSInteger indexNonWrap = [searchString lineRangeForRange:NSMakeRange(idx, 0)].location;
- NSInteger maxRangeVisibleRange = NSMaxRange([textString lineRangeForRange:NSMakeRange(NSMaxRange(visibleRange), 0)]); // Set it to just after the last glyph on the last visible line
- NSInteger numberOfGlyphsInTextString = [layoutManager numberOfGlyphs];
- BOOL oneMoreTime = NO;
- if (numberOfGlyphsInTextString != 0) {
- unichar lastGlyph = [textString characterAtIndex:numberOfGlyphsInTextString - 1];
- if (lastGlyph == '\n' || lastGlyph == '\r') {
- oneMoreTime = YES; // Continue one more time through the loop if the last glyph isn't newline
- }
- }
- NSMutableString *lineNumbersString = [[[NSMutableString alloc] init] autorelease];
-
- int textLine = 0;
- NSMutableArray* textLineBreakpoints = [NSMutableArray array];
-
- // generate line number string
- while (indexNonWrap <= maxRangeVisibleRange) {
+ @try {
+ SMLTextView *textView = [clipView documentView];
- // wrap or not
- if (idx == indexNonWrap) {
- lineNumber++;
- [lineNumbersString appendFormat:@"%li\n", (long)lineNumber];
- textLine++;
+ if ([[document valueForKey:MGSFOShowLineNumberGutter] boolValue] == NO || textView == nil) {
+ if (checkWidth == YES && recolour == YES) {
+ [[document valueForKey:ro_MGSFOSyntaxColouring] pageRecolourTextView:textView];
+ }
+ goto allDone;
+ }
+
+ NSScrollView *scrollView = (NSScrollView *)[clipView superview];
+ addToScrollPoint = 0;
+ if (scrollView == [document valueForKey:ro_MGSFOScrollView]) {
+ gutterScrollView = [document valueForKey:ro_MGSFOGutterScrollView];
+ } else {
+ goto allDone;
+ }
+
+ // get break points from delegate
+ NSSet* breakpoints = NULL;
+ id breakpointDelegate = [[MGSFragaria currentInstance] objectForKey:MGSFOBreakpointDelegate];
+ if (breakpointDelegate && [breakpointDelegate respondsToSelector:@selector(breakpointsForFile:)])
+ {
+ breakpoints = [breakpointDelegate breakpointsForFile:[gutterScrollView.documentView fileName]];
+ }
+
+ NSLayoutManager *layoutManager = [textView layoutManager];
+ NSRect visibleRect = [[scrollView contentView] documentVisibleRect];
+ NSRange visibleRange = [layoutManager glyphRangeForBoundingRect:visibleRect inTextContainer:[textView textContainer]];
+ NSInteger location = visibleRange.location;
+ NSString *textString = [textView string];
+ if (location == NSNotFound) location = textString.length;
+ NSString *searchString = [textString substringWithRange:NSMakeRange(0, location)];
+
+ for (idx = 0, lineNumber = 0; idx < (NSInteger)visibleRange.location; lineNumber++) {
+ idx = NSMaxRange([searchString lineRangeForRange:NSMakeRange(idx, 0)]);
+ }
+
+ NSInteger indexNonWrap = [searchString lineRangeForRange:NSMakeRange(idx, 0)].location;
+ NSInteger maxRangeVisibleRange = NSMaxRange([textString lineRangeForRange:NSMakeRange(NSMaxRange(visibleRange), 0)]); // Set it to just after the last glyph on the last visible line
+ NSInteger numberOfGlyphsInTextString = [layoutManager numberOfGlyphs];
+ BOOL oneMoreTime = NO;
+ if (numberOfGlyphsInTextString != 0) {
+ unichar lastGlyph = [textString characterAtIndex:numberOfGlyphsInTextString - 1];
+ if (lastGlyph == '\n' || lastGlyph == '\r') {
+ oneMoreTime = YES; // Continue one more time through the loop if the last glyph isn't newline
+ }
+ }
+ NSMutableString *lineNumbersString = [[[NSMutableString alloc] init] autorelease];
+
+ int textLine = 0;
+ NSMutableArray* textLineBreakpoints = [NSMutableArray array];
+
+ // generate line number string
+ while (indexNonWrap <= maxRangeVisibleRange) {
- // flag breakpoints
- if ([breakpoints containsObject:[NSNumber numberWithInt:(int)lineNumber]])
- {
- [textLineBreakpoints addObject:[NSNumber numberWithInt:textLine]];
+ // wrap or not
+ if (idx == indexNonWrap) {
+ lineNumber++;
+ [lineNumbersString appendFormat:@"%li\n", (long)lineNumber];
+ textLine++;
+
+ // flag breakpoints
+ if ([breakpoints containsObject:[NSNumber numberWithInt:(int)lineNumber]])
+ {
+ [textLineBreakpoints addObject:[NSNumber numberWithInt:textLine]];
+ }
+ } else {
+ [lineNumbersString appendFormat:@"%C\n", (unsigned short)0x00B7];
+ indexNonWrap = idx;
+ textLine++;
}
- } else {
- [lineNumbersString appendFormat:@"%C\n", (unsigned short)0x00B7];
- indexNonWrap = idx;
- textLine++;
- }
-
- if (idx < maxRangeVisibleRange) {
- [layoutManager lineFragmentRectForGlyphAtIndex:idx effectiveRange:&range];
- idx = NSMaxRange(range);
- indexNonWrap = NSMaxRange([textString lineRangeForRange:NSMakeRange(indexNonWrap, 0)]);
- } else {
- idx++;
- indexNonWrap ++;
- }
-
- if (idx == numberOfGlyphsInTextString && !oneMoreTime) {
- break;
- }
- }
-
- // check width is okay
- if (checkWidth == YES) {
- NSInteger widthOfStringInGutter = [lineNumbersString sizeWithAttributes:self.attributes].width;
-
- if (widthOfStringInGutter > ([[document valueForKey:MGSFOGutterWidth] integerValue] - 14)) { // Check if the gutterTextView has to be resized
- [document setValue:[NSNumber numberWithInteger:widthOfStringInGutter + 20] forKey:MGSFOGutterWidth]; // Make it bigger than need be so it doesn't have to resized soon again
- if ([[document valueForKey:MGSFOShowLineNumberGutter] boolValue] == YES) {
- gutterWidth = [[document valueForKey:MGSFOGutterWidth] integerValue];
- } else {
- gutterWidth = 0;
- }
- currentViewBounds = [[gutterScrollView superview] bounds];
- [scrollView setFrame:NSMakeRect(gutterWidth, 0, currentViewBounds.size.width - gutterWidth, currentViewBounds.size.height)];
-
- [gutterScrollView setFrame:NSMakeRect(0, 0, [[document valueForKey:MGSFOGutterWidth] integerValue], currentViewBounds.size.height)];
- }
- }
-
- if (recolour == YES) {
- [[document valueForKey:ro_MGSFOSyntaxColouring] pageRecolourTextView:textView];
- }
-
- // Fix flickering while rubber banding: Only change the text, if NOT rubber banding.
- if (visibleRect.origin.y >= 0.0f && visibleRect.origin.y <= textView.frame.size.height - visibleRect.size.height) {
- [[gutterScrollView documentView] setString:lineNumbersString];
+
+ if (idx < maxRangeVisibleRange) {
+ [layoutManager lineFragmentRectForGlyphAtIndex:idx effectiveRange:&range];
+ idx = NSMaxRange(range);
+ indexNonWrap = NSMaxRange([textString lineRangeForRange:NSMakeRange(indexNonWrap, 0)]);
+ } else {
+ idx++;
+ indexNonWrap ++;
+ }
+
+ if (idx == numberOfGlyphsInTextString && !oneMoreTime) {
+ break;
+ }
+ }
+
+ // check width is okay
+ if (checkWidth == YES) {
+ NSInteger widthOfStringInGutter = [lineNumbersString sizeWithAttributes:self.attributes].width;
+
+ if (widthOfStringInGutter > ([[document valueForKey:MGSFOGutterWidth] integerValue] - 14)) { // Check if the gutterTextView has to be resized
+ [document setValue:[NSNumber numberWithInteger:widthOfStringInGutter + 20] forKey:MGSFOGutterWidth]; // Make it bigger than need be so it doesn't have to resized soon again
+ if ([[document valueForKey:MGSFOShowLineNumberGutter] boolValue] == YES) {
+ gutterWidth = [[document valueForKey:MGSFOGutterWidth] integerValue];
+ } else {
+ gutterWidth = 0;
+ }
+ currentViewBounds = [[gutterScrollView superview] bounds];
+ [scrollView setFrame:NSMakeRect(gutterWidth, 0, currentViewBounds.size.width - gutterWidth, currentViewBounds.size.height)];
+
+ [gutterScrollView setFrame:NSMakeRect(0, 0, [[document valueForKey:MGSFOGutterWidth] integerValue], currentViewBounds.size.height)];
+ }
+ }
+
+ if (recolour == YES) {
+ [[document valueForKey:ro_MGSFOSyntaxColouring] pageRecolourTextView:textView];
+ }
+
+ // Fix flickering while rubber banding: Only change the text, if NOT rubber banding.
+ if (visibleRect.origin.y >= 0.0f && visibleRect.origin.y <= textView.frame.size.height - visibleRect.size.height) {
+ [[gutterScrollView documentView] setString:lineNumbersString];
+ }
+
+ // set breakpoint lines
+ [[gutterScrollView documentView] setBreakpointLines:textLineBreakpoints];
+
+ [[gutterScrollView contentView] setBoundsOrigin:zeroPoint]; // To avert an occasional bug which makes the line numbers disappear
+ currentLineHeight = (NSInteger)[textView lineHeight];
+ if ((NSInteger)visibleRect.origin.y != 0 && currentLineHeight != 0) {
+ CGFloat y = ((NSInteger)visibleRect.origin.y % currentLineHeight) + addToScrollPoint; // Align the line numbers with the text.
+
+ // Don't align, but directly calculate the offset, when rubber banding.
+ if (visibleRect.origin.y < 0.0f)
+ y = visibleRect.origin.y;
+ else if (visibleRect.origin.y > textView.frame.size.height - visibleRect.size.height)
+ y = visibleRect.origin.y - (textView.frame.size.height - visibleRect.size.height);
+
+ [[gutterScrollView contentView] scrollToPoint:NSMakePoint(0, y)];
+ }
+ } @catch (NSException *e) {
+ NSLog(@"Exception: %@ %s %s", e, __FILE__, __FUNCTION__);
}
-
- // set breakpoint lines
- [[gutterScrollView documentView] setBreakpointLines:textLineBreakpoints];
-
- [[gutterScrollView contentView] setBoundsOrigin:zeroPoint]; // To avert an occasional bug which makes the line numbers disappear
- currentLineHeight = (NSInteger)[textView lineHeight];
- if ((NSInteger)visibleRect.origin.y != 0 && currentLineHeight != 0) {
- CGFloat y = ((NSInteger)visibleRect.origin.y % currentLineHeight) + addToScrollPoint; // Align the line numbers with the text.
-
- // Don't align, but directly calculate the offset, when rubber banding.
- if (visibleRect.origin.y < 0.0f)
- y = visibleRect.origin.y;
- else if (visibleRect.origin.y > textView.frame.size.height - visibleRect.size.height)
- y = visibleRect.origin.y - (textView.frame.size.height - visibleRect.size.height);
-
- [[gutterScrollView contentView] scrollToPoint:NSMakePoint(0, y)];
- }
-
allDone:
self.updatingLineNumbersForClipView = nil;
Please sign in to comment.
Something went wrong with that request. Please try again.