summaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea/blog/md/ItalicsParser.kt
blob: afecf798ce8af5d76789a15a97bc6f2b09d4b914 (plain)
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package moe.nea.blog.md

object ItalicsParser : InlineParser {
    override fun detect(lookback: MarkdownFormat, rest: String): Boolean {
        return lookback is Whitespace && "\\*+[^ ].*".toRegex().matches(rest)
    }

    override val specialSyntax: Set<Char>
        get() = setOf('*')

    override fun parse(parser: MarkdownParser, text: String): Pair<MarkdownFormat, String> {
        var firstStarCount = 0
        while (firstStarCount in text.indices) {
            if (text[firstStarCount] != '*') break
            firstStarCount++
        }

        if (firstStarCount < 1 || firstStarCount > 3) error("Invalid italics/bold sequence")

        val isBold = firstStarCount >= 2
        val isItalics = (firstStarCount % 2) == 1

        val firstSequence = mutableListOf<MarkdownFormat>()
        var remainingText = text.substring(firstStarCount)
        var lastToken: MarkdownFormat = Begin() // TODO: dedicated begin token
        while (true) {
            val (element, next) = parser.parseInlineTextOnce(lastToken, remainingText)
            remainingText = next
            lastToken = element
            firstSequence.add(element)
            if (element !is Whitespace && next.startsWith("*")) break
        }

        var secondStarCount = 0
        while (secondStarCount in remainingText.indices) {
            if (remainingText[secondStarCount] != '*') break
            secondStarCount++
        }

        if (secondStarCount > firstStarCount)
            secondStarCount = firstStarCount
        if (secondStarCount < 1) error("Invalid italics/bold sequence")

        remainingText = remainingText.substring(secondStarCount)
        var firstElement = parser.collapseInlineFormat(firstSequence)
        if (secondStarCount == 2)
            firstElement = Bold(firstElement)
        if (secondStarCount == 1)
            firstElement = Italics(firstElement)
        if (secondStarCount == firstStarCount) {
            if (isBold)
                firstElement = Bold(firstElement)
            if (isItalics)
                firstElement = Italics(firstElement)
            return Pair(firstElement, remainingText)
        }

        val secondSequence = mutableListOf<MarkdownFormat>()
        lastToken = Begin()
        while (true) {
            val (element, next) = parser.parseInlineTextOnce(lastToken, remainingText)
            remainingText = next
            lastToken = element
            secondSequence.add(element)
            if (element !is Whitespace && next.startsWith("*")) break
        }

        var thirdStarCount = 0
        while (thirdStarCount in remainingText.indices) {
            if (remainingText[thirdStarCount] != '*') break
            thirdStarCount++
        }

        if (thirdStarCount > firstStarCount - secondStarCount)
            thirdStarCount = firstStarCount

        remainingText = remainingText.substring(thirdStarCount)

        if (thirdStarCount != firstStarCount - secondStarCount) {
            error("Invalid italics/bold sequence")
        }
        var secondElement = parser.collapseInlineFormat(secondSequence)
        var combined: MarkdownFormat = FormatSequence(firstElement, secondElement)
        if (thirdStarCount == 1)
            combined = Italics(combined)
        if (thirdStarCount == 2)
            combined = Bold(combined)
        return Pair(combined, remainingText)
    }

}