gnustep / libs-gui

The GNUstep gui library is a library of graphical user interface classes written completely in the Objective-C language; the classes are based upon Apple's Cocoa framework (which came from the OpenStep specification). *** Larger patches require copyright assignment to FSF. please file bugs here. ***
http://www.gnustep.org
GNU General Public License v3.0
279 stars 103 forks source link

NSAttributedString rangeOfTextList:atIndex: locks up on adjacent lists #295

Open fjmilens3 opened 2 months ago

fjmilens3 commented 2 months ago

The current rangeOfTextList:atIndex: implementation will lock up when two paragraphs with different styles and different textlists are adjacent. I found this while looking into #294 (the issue is far more likely to surface when #294 is fixed because attribute ranges aren't incorrectly merged). A fix for #293 would also likely depend on this being fixed.

The following example will reproduce the issue:

- (void) causeRangeOfTextListProblem 
{
  // Create two lists 
  NSTextList *list1 = [[NSTextList alloc] initWithMarkerFormat: @"{decimal}" options: 0];
  NSTextList *list2 = [[NSTextList alloc] initWithMarkerFormat: @"{box}" options: 0];

  // Create two paragraph styles
  NSMutableParagraphStyle *style1 = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
  NSMutableParagraphStyle *style2 = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];

  // Change the paragraph styles so that they don't get merged together (equality testing problems)
  [style1 setHeadIndent: 36.0];
  [style2 setHeadIndent: 72.0];

  // Use one list per paragraph style
  [style1 setTextLists: [NSArray arrayWithObject: list1]];
  [style2 setTextLists: [NSArray arrayWithObject: list2]];

  // Create an attributed string for each list
  NSAttributedString *str1 = [[NSAttributedString alloc] 
    initWithString: [NSString stringWithFormat: @"%@ list1\n", [list1 markerForItemNumber: 1]] 
    attributes: [NSDictionary dictionaryWithObject: style1 forKey: NSParagraphStyleAttributeName]];
  NSAttributedString *str2 = [[NSAttributedString alloc] 
    initWithString: [NSString stringWithFormat: @"%@ list2\n", [list2 markerForItemNumber: 1]] 
    attributes: [NSDictionary dictionaryWithObject: style2 forKey: NSParagraphStyleAttributeName]];

  // Append the attributed strings to the text storage
  NSTextStorage *textStorage = [textView textStorage];
  [textStorage appendAttributedString: str1];
  [textStorage appendAttributedString: str2];

  // Attempt to find the list range for one of the two adjacent lists
  // This causes a problem and the call will not return
  NSLog(@"Preparing to find list range...");

  NSRange range = [textStorage rangeOfTextList: list2 atIndex: [textStorage length] - 5];

  NSLog(@"Found range %@", NSStringFromRange(range));
}

I'm guessing it's getting stuck in an infinite loop in one of two places in the code. Perhaps not finding the specified list in the current textLists should also be a terminating condition for the loops: What if we get the style and the textLists, but don't advance the effective range because the list we have isn't included in the textLists, so the loop repeats forever.

https://github.com/gnustep/libs-gui/blob/fb61a0f287f545f4b3dc3aa7c5244572ba016d33/Source/NSAttributedString.m#L1170-L1225

Similar implementations exist for text blocks and text tables which may have the same problem, but I haven't looked at those at all.

(I can try to put together a PR for these issues if there's interest in your end, but I just installed GNUStep a couple of days ago and don't have a proper build/development set up yet.)

fredkiefer commented 2 months ago

This sounds like a serious issue, having a PR for it would be great.