laptobbe / TSMarkdownParser

MIT License
199 stars 76 forks source link

Apply NSParagraphStyleAttributeName to list items via listAttributes #50

Open nateirwin opened 8 years ago

nateirwin commented 8 years ago

I'm trying to add consistent indentation to ordered and unordered list items coming down in a Markdown string. If I change the defaultAttributes property on the TSMarkdownParser instance, adding an NSParagraphStyleAttributeName, the indentation settings work but are applied to all of the Markdown text. What I'm hoping to do is only apply the NSParagraphStyleAttributeName to the text that's associated with a Markdown list.

Pared down code here:

NSMutableParagraphStyle *listStyle;
listStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[listStyle setDefaultTabInterval:18];
[listStyle setFirstLineHeadIndent:0];
[listStyle setHeadIndent:18];
[listStyle setLineSpacing:7.5];
[listStyle setTabStops:@[ [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:18 options:nil] ]];
NSString *markdown = @"## Title\n\n- Item 1\n- Item 2\n- Item 3\n\n1. Item 1\n2. Item 2\n3. Item 3\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed interdum tortor odio, vitae porttitor lacus dapibus non. Etiam sodales euismod sapien a porta. Vivamus lectus ipsum, ultrices vel efficitur eget, sollicitudin in libero. Duis posuere felis velit, sit amet sodales nulla pharetra gravida. Aenean non justo venenatis, auctor nibh ut, hendrerit tortor.";
TSMarkdownParser *parser = [TSMarkdownParser standardParser];

parser.defaultAttributes = @{
    // This works, but affects all of the text, including text that isn't part of a list.
    // NSParagraphStyleAttributeName: listStyle
};
parser.listAttributes = @[
    // This does not affect the styling of the list items. Perhaps it's getting overridden?
    @{ NSParagraphStyleAttributeName: listStyle }
];

NSAttributedString *string = [parser attributedStringFromMarkdown:markdown];

Hopefully this explanation makes sense. I'm an Objective-C newb.

Coeur commented 8 years ago

Hello @nateirwin, defaultAttributes is for the whole attributed string, and listAttributes is only for list items.

I haven't tried NSParagraphStyleAttributeName before, so I will make some tests tonight. But to make things obvious, you can try this in the mean time:

parser.listAttributes = @[
    @{ NSForegroundColorAttributeName: [UIColor redColor] }
];
nateirwin commented 8 years ago

Hey @Coeur, thanks for getting back to me.

Adding the code from your post does work correctly, so it definitely seems like there's something going on with NSParagraphStyleAttributeName. I'll try to dig in some more, but any help is much appreciated!

Coeur commented 8 years ago

@nateirwin, did you find a reason or a workaround?

nateirwin commented 8 years ago

Nope, no more progress on this. It isn't a show stopper for us, though it would be nice to be able to apply different styling to lists.

phatmann commented 8 years ago

This seems to be an issue with NSAttributedString. Applying the paragraph style for each bulleted line does not work. Instead, the style needs to be applied across all the bulleted lines. A workaround might be to use a separate attributed string for bulleted lists.

kyledold commented 7 years ago

@Coeur Any progress on this issue? or workaround?

BobDG commented 7 years ago

@kyledold @Coeur actually this works fine right now in my App if I simply set it for the listAttributes. Like this:

NSMutableParagraphStyle style = [NSMutableParagraphStyle new]; style.headIndent = 28; NSDictionary listAttributes = @{NSFontAttributeName:[UIFont fontWithName:kFontLatoRegular size:16.0f], NSForegroundColorAttributeName:[AppAppearance textDarkGray], NSParagraphStyleAttributeName:style}; parser.listAttributes = @[listAttributes];

So I think this issue can be closed?

kyledold commented 7 years ago

So my issue was setting the NSParagraphStyleAttributeName attribute for the "listAttributes" property of the parser wasn't having any effect, even after trying @BobDG's example.

The only way I could get this to work was to set the NSParagraphStyleAttributeName attribute of the "defaultAttributes" property.

BobDG commented 7 years ago

@kyledold hm strange, it works perfectly for me. Only for bullet lists unordered though, nut numbered lists because the regex doesn't take those into account.

kyledold commented 7 years ago

@BobDG which version are you using? I've got v2.1.3

BobDG commented 7 years ago

@kyledold me too.. what you can check is perhaps if the regex actually captures your lists by putting some logs at the right places?

khill25 commented 7 years ago

I am also having this issue. Version 2.1.3. Things look great when applying the style to the whole string using defaultAttributes but using listAttributes doesn't seem to do anything once the string wraps.

I think this may be a mutable string bug though. I have experienced issues in the past trying to apply multiple paragraph styles to a attributed string.

Kind of a huge bummer because it makes an existing view more complicated to break out all the strings into their own labels.

kyledold commented 7 years ago

@khill25 @BobDG I found the only way to get NSParagraphStyleAttributeName set for list elements is to create your own version of the TSMarkdownParser object and then in the init method where you add all the parsing logic you need to replace the "addListParsingWithMaxLevel" method with the following:

+ (instancetype)standardParser {

  LURMarkDownParser *defaultParser = [self new];

  ....

  NSMutableParagraphStyle *listStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
  [listStyle setDefaultTabInterval:18];
  [listStyle setFirstLineHeadIndent:0];
  [listStyle setHeadIndent:28];

  [defaultParser addListParsingWithMaxLevel:0 leadFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range, NSUInteger level) {
    NSMutableString *listString = [NSMutableString string];
    while (--level)
     [listString appendString:@"\t"];
   [listString appendString:@"•\t"];
   [attributedString replaceCharactersInRange:range withString:listString];

    // Apply paragraph style attributes to string 
    [attributedString addAttributes:@{NSFontAttributeName:[UIFont fontWithName:defaultFontFamily size:16], NSParagraphStyleAttributeName: listStyle} range:range];

  } textFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range, NSUInteger level) {
    [LURMarkDownParser addAttributes:weakParser.listAttributes atIndex:level - 1 toString:attributedString range:range];
  }];

  .....

}

Not the cleanest solution but it works.

BobDG commented 7 years ago

@kyledold ok nice you got it working! If I look at the code I still don't understand why mine is working without any adaptations and that you need these changes. But I'm glad it works for you now :)