mirror of
https://github.com/zhigang1992/GitHawk.git
synced 2026-06-02 06:30:26 +08:00
parse checkboxes in MMMarkdown (#1269)
This commit is contained in:
@@ -65,9 +65,8 @@ func CreateCommentModels(
|
||||
|
||||
let emojiMarkdown = replaceGithubEmojiRegex(string: markdown)
|
||||
let replaceHTMLentities = emojiMarkdown.removingHTMLEntities
|
||||
let replaceCheckmarks = annotateCheckmarks(markdown: replaceHTMLentities)
|
||||
|
||||
guard let document = createCommentAST(markdown: replaceCheckmarks)
|
||||
guard let document = createCommentAST(markdown: replaceHTMLentities)
|
||||
else { return [emptyDescriptionModel(width: width)] }
|
||||
|
||||
var results = [ListDiffable]()
|
||||
@@ -267,6 +266,16 @@ func travelAST(
|
||||
if substring != newlineString || listLevel == 0 {
|
||||
attributedString.append(NSAttributedString(string: substring, attributes: pushedAttributes))
|
||||
}
|
||||
} else if element.type == .checkbox {
|
||||
let appendDisabled = viewerCanUpdate ? "" : "-disabled"
|
||||
if let image = UIImage(named: (element.checked ? "checked" : "unchecked") + appendDisabled) {
|
||||
let attachment = NSTextAttachment()
|
||||
attachment.image = image
|
||||
// nudge bounds to align better with baseline text
|
||||
attachment.bounds = CGRect(x: 0, y: -2, width: image.size.width, height: image.size.height)
|
||||
attributedString.append(NSAttributedString(attachment: attachment))
|
||||
attributedString.append(NSAttributedString(string: " ", attributes: pushedAttributes))
|
||||
}
|
||||
} else if element.type == .listItem {
|
||||
// append list styles at the beginning of each list item
|
||||
let isInsideBulletedList = element.parent?.type == .bulletedList
|
||||
@@ -404,40 +413,6 @@ func shortenGitHubLinks(attributedString: NSAttributedString,
|
||||
return mutableAttributedString
|
||||
}
|
||||
|
||||
private let checkedIdentifier = ">/CHECKED>/"
|
||||
private let uncheckedIdentifier = ">/UNCHECKED>/"
|
||||
private let checkmarkRegex = try! NSRegularExpression(pattern: "^- (\\[([ |x])\\])", options: [.anchorsMatchLines])
|
||||
private func annotateCheckmarks(markdown: String) -> String {
|
||||
let matches = checkmarkRegex.matches(in: markdown, options: [], range: markdown.nsrange)
|
||||
let result = NSMutableString(string: markdown)
|
||||
for match in matches.reversed() {
|
||||
let checked = markdown.substring(with: match.range(at: 2)) == "x"
|
||||
result.replaceCharacters(in: match.range(at: 1), with: checked ? checkedIdentifier : uncheckedIdentifier)
|
||||
}
|
||||
return result as String
|
||||
}
|
||||
|
||||
private let checkmarkIdentifierRegex = try! NSRegularExpression(pattern: "(\(checkedIdentifier)|\(uncheckedIdentifier))", options: [])
|
||||
func addCheckmarkAttachments(
|
||||
attributedString: NSAttributedString,
|
||||
viewerCanUpdate: Bool
|
||||
) -> NSAttributedString {
|
||||
let string = attributedString.string
|
||||
let matches = checkmarkIdentifierRegex.matches(in: string, options: [], range: string.nsrange)
|
||||
let result = NSMutableAttributedString(attributedString: attributedString)
|
||||
for match in matches.reversed() {
|
||||
let checked = string.substring(with: match.range) == checkedIdentifier
|
||||
let attachment = NSTextAttachment()
|
||||
let appendDisabled = viewerCanUpdate ? "" : "-disabled"
|
||||
guard let image = UIImage(named: (checked ? "checked" : "unchecked") + appendDisabled) else { continue }
|
||||
attachment.image = image
|
||||
// nudge bounds to align better with baseline text
|
||||
attachment.bounds = CGRect(x: 0, y: -2, width: image.size.width, height: image.size.height)
|
||||
result.replaceCharacters(in: match.range, with: NSAttributedString(attachment: attachment))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func createTextModelUpdatingGitHubFeatures(
|
||||
attributedString: NSAttributedString,
|
||||
width: CGFloat,
|
||||
@@ -448,11 +423,10 @@ func createTextModelUpdatingGitHubFeatures(
|
||||
|
||||
let issues = updateIssueShorthand(attributedString: attributedString, options: options)
|
||||
let shorten = shortenGitHubLinks(attributedString: issues, options: options)
|
||||
let checkmarked = addCheckmarkAttachments(attributedString: shorten, viewerCanUpdate: viewerCanUpdate)
|
||||
|
||||
return NSAttributedStringSizing(
|
||||
containerWidth: width,
|
||||
attributedText: checkmarked,
|
||||
attributedText: shorten,
|
||||
inset: inset
|
||||
)
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ typedef NS_ENUM(NSInteger, MMElementType)
|
||||
MMElementTypeTableRow,
|
||||
MMElementTypeTableRowCell,
|
||||
MMElementTypeUsername,
|
||||
MMElementTypeCheckbox,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, MMTableCellAlignment)
|
||||
@@ -79,6 +80,7 @@ typedef NS_ENUM(NSInteger, MMTableCellAlignment)
|
||||
@property (copy, nonatomic, nullable) NSString *stringValue;
|
||||
@property (assign, nonatomic) NSUInteger numberedListPosition;
|
||||
@property (copy, nonatomic, nullable) NSString *username;
|
||||
@property (assign, nonatomic) BOOL checked;
|
||||
|
||||
@property (weak, nonatomic, nullable) MMElement *parent;
|
||||
@property (copy, nonatomic, nonnull) NSArray<MMElement *> *children;
|
||||
|
||||
@@ -82,6 +82,8 @@ static NSString * __MMStringFromElementType(MMElementType type)
|
||||
return @"table-header-cell";
|
||||
case MMElementTypeUsername:
|
||||
return @"username";
|
||||
case MMElementTypeCheckbox:
|
||||
return @"checkbox";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ typedef NS_OPTIONS(NSUInteger, MMMarkdownExtensions)
|
||||
MMMarkdownExtensionsUnderscoresInWords = 1 << 9,
|
||||
|
||||
MMMarkdownExtensionsUsernames = 1 << 10,
|
||||
MMMarkdownExtensionsCheckboxes = 1 << 11,
|
||||
|
||||
MMMarkdownExtensionsGitHubFlavored = MMMarkdownExtensionsAutolinkedURLs|MMMarkdownExtensionsFencedCodeBlocks|MMMarkdownExtensionsHardNewlines|MMMarkdownExtensionsStrikethroughs|MMMarkdownExtensionsTables|MMMarkdownExtensionsUnderscoresInWords|MMMarkdownExtensionsUsernames,
|
||||
MMMarkdownExtensionsGitHubFlavored = MMMarkdownExtensionsAutolinkedURLs|MMMarkdownExtensionsFencedCodeBlocks|MMMarkdownExtensionsHardNewlines|MMMarkdownExtensionsStrikethroughs|MMMarkdownExtensionsTables|MMMarkdownExtensionsUnderscoresInWords|MMMarkdownExtensionsUsernames|MMMarkdownExtensionsCheckboxes,
|
||||
};
|
||||
|
||||
|
||||
@@ -910,7 +910,13 @@ static NSString * __HTMLEntityForCharacter(unichar character)
|
||||
}
|
||||
else
|
||||
{
|
||||
element.children = [self.spanParser parseSpansInBlockElement:element withScanner:preListScanner];
|
||||
NSMutableArray *children = [NSMutableArray new];
|
||||
MMElement *checkbox = [self _parseListCheckboxWithScanner:preListScanner];
|
||||
if (checkbox) {
|
||||
[children addObject:checkbox];
|
||||
}
|
||||
[children addObjectsFromArray:[self.spanParser parseSpansInBlockElement:element withScanner:preListScanner]];
|
||||
element.children = children;
|
||||
}
|
||||
|
||||
element.children = [element.children arrayByAddingObjectsFromArray:[self _parseElementsWithScanner:postListScanner]];
|
||||
@@ -924,7 +930,13 @@ static NSString * __HTMLEntityForCharacter(unichar character)
|
||||
}
|
||||
else
|
||||
{
|
||||
element.children = [self.spanParser parseSpansInBlockElement:element withScanner:innerScanner];
|
||||
NSMutableArray *children = [NSMutableArray new];
|
||||
MMElement *checkbox = [self _parseListCheckboxWithScanner:innerScanner];
|
||||
if (checkbox) {
|
||||
[children addObject:checkbox];
|
||||
}
|
||||
[children addObjectsFromArray:[self.spanParser parseSpansInBlockElement:element withScanner:innerScanner]];
|
||||
element.children = children;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -932,6 +944,66 @@ static NSString * __HTMLEntityForCharacter(unichar character)
|
||||
return element;
|
||||
}
|
||||
|
||||
- (MMElement *)_parseListCheckboxWithScanner:(MMScanner *)scanner
|
||||
{
|
||||
[scanner skipWhitespace];
|
||||
|
||||
const NSRange range = NSMakeRange(scanner.location, 3);
|
||||
NSString *string = scanner.string;
|
||||
|
||||
if (string.length - range.location < range.length) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *substring = [string substringWithRange:range];
|
||||
BOOL checked = NO;
|
||||
|
||||
if ([substring isEqualToString:@"[x]"] || [substring isEqualToString:@"[X]"]) {
|
||||
checked = YES;
|
||||
} else if (![substring isEqualToString:@"[ ]"]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
scanner.location += range.length;
|
||||
|
||||
MMElement *element = [MMElement new];
|
||||
element.type = MMElementTypeCheckbox;
|
||||
element.range = range;
|
||||
element.checked = checked;
|
||||
|
||||
return element;
|
||||
|
||||
// const NSInteger start = scanner.startLocation;
|
||||
//
|
||||
// if (scanner.nextCharacter != '[') {
|
||||
// return nil;
|
||||
// }
|
||||
//
|
||||
// [scanner advance];
|
||||
//
|
||||
// BOOL checked = NO;
|
||||
// if (scanner.nextCharacter == 'x') {
|
||||
// checked = YES;
|
||||
// } else if (scanner.nextCharacter != ' ') {
|
||||
// return nil;
|
||||
// }
|
||||
//
|
||||
// [scanner advance];
|
||||
//
|
||||
// if (scanner.nextCharacter != ']') {
|
||||
// return nil;
|
||||
// }
|
||||
//
|
||||
// [scanner advance];
|
||||
//
|
||||
// MMElement *element = [MMElement new];
|
||||
// element.type = MMElementTypeCheckboxes;
|
||||
// element.range = NSMakeRange(start, 3);
|
||||
// element.checked = checked;
|
||||
//
|
||||
// return element;
|
||||
}
|
||||
|
||||
- (MMElement *)_parseListWithScanner:(MMScanner *)scanner
|
||||
{
|
||||
[scanner beginTransaction];
|
||||
|
||||
Reference in New Issue
Block a user