|
| 1 | +package components |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "regexp" |
| 6 | +) |
| 7 | + |
| 8 | +var rReg = regexp.MustCompile(`\r`) |
| 9 | + |
| 10 | +var mdSyntax = map[string]string{ |
| 11 | + "\\*": "*", |
| 12 | + "_": "_", |
| 13 | + "\\|": "|", |
| 14 | + "`": "`", |
| 15 | + "~": "~", |
| 16 | +} |
| 17 | + |
| 18 | +var mdSpreadInline = createMdInfo("`", "`", 1, NB_Spread, "", "") |
| 19 | + |
| 20 | +var ( |
| 21 | + mdBold = createMdInfo(`\*`, "*", 2, NB_Spread, "b", "") |
| 22 | + mdUnderline = createMdInfo(`_`, "_", 2, NB_Spread, "u", "") |
| 23 | + mdItal1 = createMdInfo(`\*`, "*", 1, NB_Ignore, "i", "") |
| 24 | + mdItal2 = createMdInfo(`_`, "_", 1, NB_Ignore, "i", "") |
| 25 | + mdStrike = createMdInfo(`~`, "~", 2, NB_Stop, "s", "") |
| 26 | + mdSpoiler = createMdInfo(`\|`, "|", 2, NB_Stop, "span", "spoiler") |
| 27 | + mdInlineCode = createMdInfo("`", "`", 1, NB_Ignore, "span", "inline-code") |
| 28 | +) |
| 29 | + |
| 30 | +var mdMap = []mdInfo{ |
| 31 | + mdBold, |
| 32 | + mdUnderline, |
| 33 | + mdItal1, |
| 34 | + mdItal2, |
| 35 | + mdStrike, |
| 36 | + mdSpoiler, |
| 37 | +} |
| 38 | + |
| 39 | +type mdInfo struct { |
| 40 | + Reg *regexp.Regexp |
| 41 | + |
| 42 | + Char byte |
| 43 | + Size int |
| 44 | + |
| 45 | + NextBehavior NextBehavior |
| 46 | + |
| 47 | + HtmlOpen string |
| 48 | + HtmlClose string |
| 49 | +} |
| 50 | + |
| 51 | +// Caller is responsibe to make sure there is a match |
| 52 | +func (m mdInfo) ReplaceAll(content *string) { |
| 53 | + skipI := 0 |
| 54 | + |
| 55 | + for skipI != len(m.Reg.FindAllStringIndex(*content, skipI+1)) { |
| 56 | + locs := m.Reg.FindAllStringIndex(*content, skipI+1) |
| 57 | + loc := locs[skipI] |
| 58 | + |
| 59 | + switch m.NextBehavior { |
| 60 | + case NB_Ignore: |
| 61 | + if loc[1] != len(*content)-1 { |
| 62 | + loc[1] -= 1 |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + locInner := []int{loc[0] + m.Size, loc[1] - m.Size} |
| 67 | + |
| 68 | + *content = (*content)[:loc[0]] + m.HtmlOpen + (*content)[locInner[0]:locInner[1]] + m.HtmlClose + (*content)[loc[1]:] |
| 69 | + } |
| 70 | + |
| 71 | +} |
| 72 | + |
| 73 | +// func (m mdInfo) |
| 74 | + |
| 75 | +type NextBehavior int8 |
| 76 | + |
| 77 | +const ( |
| 78 | + // The things must put on the out most characters of the group |
| 79 | + NB_Spread NextBehavior = iota |
| 80 | + // Stop at the first match, ie. |
| 81 | + // |
| 82 | + // ~~~asdasdad~~~ |
| 83 | + // |
| 84 | + // ^^_________^^_ |
| 85 | + NB_Stop |
| 86 | + // Ignores the match if there is another one of it's symbols after it. ie. |
| 87 | + // |
| 88 | + // _aa_: ok |
| 89 | + // |
| 90 | + // __aa_: ok |
| 91 | + // |
| 92 | + // _^__^ |
| 93 | + // |
| 94 | + // _aa__: not ok |
| 95 | + NB_Ignore |
| 96 | +) |
| 97 | + |
| 98 | +func MDToHTML(content string) (string, bool) { |
| 99 | + inlineCodeMap := map[int]string{} |
| 100 | + escapedSyntaxMap := map[int]string{} |
| 101 | + icI := 0 |
| 102 | + esI := 0 |
| 103 | + |
| 104 | + for syntax := range mdSyntax { |
| 105 | + reg := regexp.MustCompile(`\\` + syntax) |
| 106 | + for reg.MatchString(content) { |
| 107 | + loc := reg.FindStringIndex(content) |
| 108 | + escapedSyntaxMap[esI] = syntax |
| 109 | + content = content[:loc[0]] + fmt.Sprintf("--\tes%v--", esI) + content[loc[1]:] |
| 110 | + esI++ |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + // inline code index |
| 115 | + for mdSpreadInline.Reg.MatchString(content) { |
| 116 | + curLoc := mdSpreadInline.Reg.FindStringIndex(content) |
| 117 | + inlineCodeMap[icI] = content[curLoc[0]:curLoc[1]] |
| 118 | + content = fmt.Sprintf("%v--\tic%v--%v", content[:curLoc[0]], icI, content[curLoc[1]:]) |
| 119 | + icI++ |
| 120 | + } |
| 121 | + |
| 122 | + bq := false |
| 123 | + if len(content) > 5 { |
| 124 | + if content[:5] == "> " { |
| 125 | + // Block quote |
| 126 | + content = content[5:] |
| 127 | + bq = true |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + for _, mdRule := range mdMap { |
| 132 | + mdRule.ReplaceAll(&content) |
| 133 | + } |
| 134 | + |
| 135 | + for i, str := range inlineCodeMap { |
| 136 | + reg := regexp.MustCompile(fmt.Sprintf(`--\tic%v--`, i)) |
| 137 | + content = reg.ReplaceAllString(content, str) |
| 138 | + } |
| 139 | + |
| 140 | + for i, str := range escapedSyntaxMap { |
| 141 | + reg := regexp.MustCompile(fmt.Sprintf(`--\tes%v--`, i)) |
| 142 | + content = reg.ReplaceAllString(content, mdSyntax[str]) |
| 143 | + } |
| 144 | + |
| 145 | + return content, bq |
| 146 | +} |
| 147 | + |
| 148 | +func createMdInfo(regEscapeChar string, oneChar string, size int, nextBehavior NextBehavior, htmlTag string, cssClass string) mdInfo { |
| 149 | + htmlAttr := "" |
| 150 | + |
| 151 | + if cssClass != "" { |
| 152 | + htmlAttr = " class=\"" + cssClass + "\"" |
| 153 | + } |
| 154 | + |
| 155 | + return mdInfo{ |
| 156 | + Reg: createEnclosed(regEscapeChar, size, nextBehavior), |
| 157 | + HtmlOpen: "<" + htmlTag + htmlAttr + ">", |
| 158 | + HtmlClose: fmt.Sprintf(`</%v>`, htmlTag), |
| 159 | + Char: []byte(oneChar)[0], |
| 160 | + NextBehavior: nextBehavior, |
| 161 | + Size: size, |
| 162 | + } |
| 163 | +} |
| 164 | + |
| 165 | +func createEnclosed(charReg string, size int, nextBehavior NextBehavior) *regexp.Regexp { |
| 166 | + strReg := "" |
| 167 | + switch nextBehavior { |
| 168 | + case NB_Ignore: |
| 169 | + totalReg := "" |
| 170 | + for i := 0; i < size; i++ { |
| 171 | + totalReg += charReg |
| 172 | + } |
| 173 | + strReg = fmt.Sprintf(`%v[^%v]+?%v([^%v]|$)`, totalReg, charReg, totalReg, charReg) |
| 174 | + case NB_Spread: |
| 175 | + strReg = fmt.Sprintf(`%v{%v,}.+?%v{%v,}`, charReg, size, charReg, size) |
| 176 | + case NB_Stop: |
| 177 | + strReg = fmt.Sprintf(`%v{%v,}.+?%v{%v}`, charReg, size, charReg, size) |
| 178 | + } |
| 179 | + |
| 180 | + return regexp.MustCompile(strReg) |
| 181 | +} |
0 commit comments