Home telegram - parseMarkdown
Post
Cancel

telegram - parseMarkdown

parseMarkdownIntoAttributedString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public func parseMarkdownIntoAttributedString(_ string: String, attributes: MarkdownAttributes, textAlignment: NSTextAlignment = .natural) -> NSAttributedString {
    let nsString = string as NSString
    let result = NSMutableAttributedString()
    var remainingRange = NSMakeRange(0, nsString.length)
    
    var bodyAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: attributes.body.font, NSAttributedString.Key.foregroundColor: attributes.body.textColor, NSAttributedString.Key.paragraphStyle: paragraphStyleWithAlignment(textAlignment)]
    if !attributes.body.additionalAttributes.isEmpty {
        for (key, value) in attributes.body.additionalAttributes {
            bodyAttributes[NSAttributedString.Key(rawValue: key)] = value
        }
    }
    
    while true {
        let range = nsString.rangeOfCharacter(from: controlStartCharactersSet, options: [], range: remainingRange)
        if range.location != NSNotFound {
            if range.location != remainingRange.location {
                result.append(NSAttributedString(string: nsString.substring(with: NSMakeRange(remainingRange.location, range.location - remainingRange.location)), attributes: bodyAttributes))
                remainingRange = NSMakeRange(range.location, remainingRange.location + remainingRange.length - range.location)
            }
            
            let character = nsString.character(at: range.location)
            if character == UInt16(("[" as UnicodeScalar).value) {
                remainingRange = NSMakeRange(range.location + range.length, remainingRange.location + remainingRange.length - (range.location + range.length))
                if let (parsedLinkText, parsedLinkContents) = parseLink(string: nsString, remainingRange: &remainingRange) {
                    var linkAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: attributes.link.font, NSAttributedString.Key.foregroundColor: attributes.link.textColor, NSAttributedString.Key.paragraphStyle: paragraphStyleWithAlignment(textAlignment)]
                    if !attributes.link.additionalAttributes.isEmpty {
                        for (key, value) in attributes.link.additionalAttributes {
                            linkAttributes[NSAttributedString.Key(rawValue: key)] = value
                        }
                    }
                    if let (attributeName, attributeValue) = attributes.linkAttribute(parsedLinkContents) {
                        linkAttributes[NSAttributedString.Key(rawValue: attributeName)] = attributeValue
                    }
                    result.append(NSAttributedString(string: parsedLinkText, attributes: linkAttributes))
                }
            } else if character == UInt16(("*" as UnicodeScalar).value) {
                if range.location + 1 != remainingRange.length {
                    let nextCharacter = nsString.character(at: range.location + 1)
                    if nextCharacter == character {
                        remainingRange = NSMakeRange(range.location + range.length + 1, remainingRange.location + remainingRange.length - (range.location + range.length + 1))
                        
                        if let bold = parseBold(string: nsString, remainingRange: &remainingRange) {
                            var boldAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: attributes.bold.font, NSAttributedString.Key.foregroundColor: attributes.bold.textColor, NSAttributedString.Key.paragraphStyle: paragraphStyleWithAlignment(textAlignment)]
                            if !attributes.body.additionalAttributes.isEmpty {
                                for (key, value) in attributes.bold.additionalAttributes {
                                    boldAttributes[NSAttributedString.Key(rawValue: key)] = value
                                }
                            }
                            result.append(NSAttributedString(string: bold, attributes: boldAttributes))
                        } else {
                            result.append(NSAttributedString(string: nsString.substring(with: NSMakeRange(remainingRange.location, 1)), attributes: bodyAttributes))
                            remainingRange = NSMakeRange(range.location + 1, remainingRange.length - 1)
                        }
                    } else {
                        if result.string.hasSuffix("\\") {
                            result.deleteCharacters(in: NSMakeRange(result.string.count - 1, 1))
                        }
                        result.append(NSAttributedString(string: nsString.substring(with: NSMakeRange(remainingRange.location, 1)), attributes: bodyAttributes))
                        remainingRange = NSMakeRange(range.location + 1, remainingRange.length - 1)
                    }
                } else {
                    result.append(NSAttributedString(string: nsString.substring(with: NSMakeRange(remainingRange.location, 1)), attributes: bodyAttributes))
                    remainingRange = NSMakeRange(range.location + 1, remainingRange.length - 1)
                }
            }
        } else {
            if remainingRange.length != 0 {
                result.append(NSAttributedString(string: nsString.substring(with: NSMakeRange(remainingRange.location, remainingRange.length)), attributes: bodyAttributes))
            }
            break
        }
    }
    return result
}

escapedPlaintextForMarkdown

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public func escapedPlaintextForMarkdown(_ string: String) -> String {
    let nsString = string as NSString
    var remainingRange = NSMakeRange(0, nsString.length)
    let result = NSMutableString()
    while true {
        let range = nsString.rangeOfCharacter(from: controlCharactersSet, options: [], range: remainingRange)
        if range.location != NSNotFound {
            if range.location - remainingRange.location > 0 {
                result.append(nsString.substring(with: NSMakeRange(remainingRange.location, range.location - remainingRange.location)))
            }
            result.append("\\")
            result.append(nsString.substring(with: NSMakeRange(range.location, range.length)))
            remainingRange = NSMakeRange(range.location + range.length, remainingRange.location + remainingRange.length - (range.location + range.length))
        } else {
            result.append(nsString.substring(with: NSMakeRange(remainingRange.location, remainingRange.length)))
            break
        }
    }
    return result as String
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private func parseLink(string: NSString, remainingRange: inout NSRange) -> (text: String, contents: String)? {
    var localRemainingRange = remainingRange
    let closingSquareBraceRange = string.range(of: "]", options: [], range: localRemainingRange)
    if closingSquareBraceRange.location != NSNotFound {
        localRemainingRange = NSMakeRange(closingSquareBraceRange.location + closingSquareBraceRange.length, remainingRange.location + remainingRange.length - (closingSquareBraceRange.location + closingSquareBraceRange.length))
        let openingRoundBraceRange = string.range(of: "(", options: [], range: localRemainingRange)
        let closingRoundBraceRange = string.range(of: ")", options: [], range: localRemainingRange)
        if openingRoundBraceRange.location == closingSquareBraceRange.location + closingSquareBraceRange.length && closingRoundBraceRange.location != NSNotFound && openingRoundBraceRange.location < closingRoundBraceRange.location {
            let linkText = string.substring(with: NSMakeRange(remainingRange.location, closingSquareBraceRange.location - remainingRange.location))
            let linkContents = string.substring(with: NSMakeRange(openingRoundBraceRange.location + openingRoundBraceRange.length, closingRoundBraceRange.location - (openingRoundBraceRange.location + openingRoundBraceRange.length)))
            remainingRange = NSMakeRange(closingRoundBraceRange.location + closingRoundBraceRange.length, remainingRange.location + remainingRange.length - (closingRoundBraceRange.location + closingRoundBraceRange.length))
            return (linkText, linkContents)
        }
    }
    return nil
}

parseBold

1
2
3
4
5
6
7
8
9
10
11
12
private func parseBold(string: NSString, remainingRange: inout NSRange) -> String? {
    var localRemainingRange = remainingRange
    let closingRange = string.range(of: "**", options: [], range: localRemainingRange)
    if closingRange.location != NSNotFound {
        localRemainingRange = NSMakeRange(closingRange.location + closingRange.length, remainingRange.location + remainingRange.length - (closingRange.location + closingRange.length))
        
        let result = string.substring(with: NSRange(location: remainingRange.location, length: closingRange.location - remainingRange.location))
        remainingRange = localRemainingRange
        return result
    }
    return nil
}
This post is licensed under CC BY 4.0 by the author.