Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Behavior difference in NSAttributeString itemNumberInTextList:atIndex: #293

Open
fjmilens3 opened this issue Sep 23, 2024 · 1 comment
Open

Comments

@fjmilens3
Copy link

fjmilens3 commented Sep 23, 2024

Per Apple documentation, NSAttributeString's itemNumberInTextList:atIndex: should return the index of the item at the specified location within the list.

The current GNUStep implementation instead returns the index position of the list within the lists for the current paragraph style. This gives you the nesting level instead of the item number.

- (NSInteger) itemNumberInTextList: (NSTextList *)list
atIndex: (NSUInteger)location
{
NSParagraphStyle *style = [self attribute: NSParagraphStyleAttributeName
atIndex: location
effectiveRange: NULL];
if (style != nil)
{
NSArray *textLists = [style textLists];
if (textLists != nil)
{
return [textLists indexOfObject: list];
}
}
return NSNotFound;
}

For an example, consider the following list:

  • Alpha
  • Beta
  • Gamma
  • Delta
  • Epsilon

For a text position anywhere in, say, "Delta," Apple's implementation would return 4 as it's the fourth item in the list. The current GNUstep implementation returns 0 as the list is the zeroth item in the paragraph style's lists array.

I've been playing around with this locally but I'd encourage someone else with access to a Mac to verify the difference as well. Note that if you write a test program of your own with Cocoa on recent Macs, you'll probably want to opt-out of TextKit 2 behavior via [textView layoutManager] for an equal comparison to GNUStep.

If it turns out to be a valid bug, some thoughts based on my current, hacky workaround: Get the entire range of the list from rangeOfTextList:atIndex:, iterate over the range looking for newline characters, and basically count paragraphs until it reaches the position we were asked about. You'd also want to check against the lists in each paragraph's style to account for things like nested lists.

@fjmilens3
Copy link
Author

For those who don't have access to a Mac, see below.

Cocoa example code for staging list data:

    // Do this to revert to TextKit 1 instead of TextKit 2
    NSLayoutManager *manager = [_textView layoutManager];
    
    NSTextList *list = [[NSTextList alloc] initWithMarkerFormat:@"{decimal}" options:0];
    
    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    
    [style setTextLists: [NSArray arrayWithObject: list]];
    
    NSMutableString *str = [[NSMutableString alloc] init];
    for (int i = 0; i < 10; i++) {
        [str appendFormat: @"%@ item\n", [list markerForItemNumber: i]];
    }
    
    NSDictionary *attrs = [NSDictionary dictionaryWithObject: style forKey: NSParagraphStyleAttributeName];
    
    NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString: str attributes: attrs];
    
    NSRange editRange = [_textView rangeForUserTextChange];
    NSTextStorage *storage = [_textView textStorage];
    [storage replaceCharactersInRange:editRange withAttributedString:attrStr];

Cocoa example code for obtaining the list index:

    NSRange range = [_textView rangeForUserTextChange];
    
    NSTextStorage *storage = [_textView textStorage];
    
    NSParagraphStyle *style = [storage attribute: NSParagraphStyleAttributeName atIndex: range.location effectiveRange: NULL];
    
    NSTextList *list = [[style textLists] lastObject];
    
    if (list != nil) {
        NSUInteger number = [storage itemNumberInTextList: list atIndex:range.location];
        
        NSLog(@"List item number: %lu", (unsigned long)number);
    } else {
        NSLog(@"No list");
    }

Cocoa Screenshot:

Screenshot 2024-09-22 at 10 57 40 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant