markdown.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package tool
  2. import (
  3. "bytes"
  4. "database/sql"
  5. "log"
  6. "net/url"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "github.com/yuin/goldmark"
  11. "github.com/yuin/goldmark/extension"
  12. "github.com/yuin/goldmark/renderer/html"
  13. )
  14. func Markdown(db *sql.DB, data map[string]string) map[string]interface{} {
  15. backlink := map[string]map[string]string{}
  16. link_count := 0
  17. raw_input := data["data"]
  18. r := regexp.MustCompile(`\[\]\(([^\(\)]+)\)`)
  19. raw_input = r.ReplaceAllStringFunc(raw_input, func(m string) string {
  20. match := r.FindStringSubmatch(m)
  21. return "[" + match[1] + "](" + match[1] + ")"
  22. })
  23. r = regexp.MustCompile(`\[([^\[\]]+)\]\(\)`)
  24. raw_input = r.ReplaceAllStringFunc(raw_input, func(m string) string {
  25. match := r.FindStringSubmatch(m)
  26. return "[" + match[1] + "](" + match[1] + ")"
  27. })
  28. input := []byte(raw_input)
  29. markdown := goldmark.New(
  30. goldmark.WithExtensions(
  31. extension.Strikethrough,
  32. extension.Table,
  33. ),
  34. goldmark.WithRendererOptions(
  35. html.WithHardWraps(),
  36. ),
  37. )
  38. var buf bytes.Buffer
  39. if err := markdown.Convert(input, &buf); err != nil {
  40. panic(err)
  41. }
  42. string_data := buf.String()
  43. code_stack := []int{}
  44. code_stack_idx := 0
  45. code_stack_end := map[string]string{}
  46. r = regexp.MustCompile(`(<code>|<\/code>)`)
  47. for idx := r.FindStringIndex(string_data); len(idx) != 0; idx = r.FindStringIndex(string_data) {
  48. if string_data[idx[0]:idx[1]] == "<code>" {
  49. code_stack = []int{idx[0], idx[1]}
  50. string_data = strings.Replace(string_data, "<code>", "<0001>", 1)
  51. } else {
  52. string_data = strings.Replace(string_data, "<0001>", "<code>", -1)
  53. code_stack_idx_str := strconv.Itoa(code_stack_idx)
  54. code_stack_end["code_"+code_stack_idx_str] = string_data[code_stack[0]:idx[1]]
  55. code_stack_idx++
  56. string_data = string_data[:code_stack[0]] + "<code_" + code_stack_idx_str + ">" + string_data[idx[1]:]
  57. }
  58. }
  59. // p := bluemonday.UGCPolicy()
  60. // string_data := p.Sanitize(string_data)
  61. r = regexp.MustCompile(`\[([^\[\]]+)\]\(([^\(\)]*)\)`)
  62. string_data = r.ReplaceAllStringFunc(string_data, func(m string) string {
  63. match := r.FindStringSubmatch(m)
  64. link := match[2]
  65. if link == "" {
  66. link = match[1]
  67. }
  68. return "<a href=\"" + link + "\">" + match[1] + "</a>"
  69. })
  70. r = regexp.MustCompile(`<code_[0-9]+>`)
  71. string_data = r.ReplaceAllStringFunc(string_data, func(m string) string {
  72. m = strings.Replace(m, "<", "", 1)
  73. m = strings.Replace(m, ">", "", 1)
  74. return code_stack_end[m]
  75. })
  76. r = regexp.MustCompile(`<a href="([^"]+)"`)
  77. string_data = r.ReplaceAllStringFunc(string_data, func(m string) string {
  78. match := r.FindStringSubmatch(m)
  79. m1, _ := regexp.MatchString(`^https?:\/\/`, match[1])
  80. if m1 {
  81. return "<a href=\"" + match[1] + "\" class=\"opennamu_link_out\" target=\"_blank\""
  82. } else {
  83. link := ""
  84. link, _ = url.QueryUnescape(match[1])
  85. if _, ok := backlink[link]; !ok {
  86. backlink[link] = map[string]string{}
  87. }
  88. var exist string
  89. stmt, err := db.Prepare(DB_change("select title from data where title = ?"))
  90. if err != nil {
  91. log.Fatal(err)
  92. }
  93. defer stmt.Close()
  94. err = stmt.QueryRow(link).Scan(&exist)
  95. if err != nil {
  96. if err == sql.ErrNoRows {
  97. exist = ""
  98. } else {
  99. log.Fatal(err)
  100. }
  101. }
  102. backlink[link][""] = ""
  103. link_count += 1
  104. class := ""
  105. if exist == "" {
  106. class = "opennamu_not_exist_link"
  107. }
  108. return "<a href=\"/w/" + match[1] + "\" class=\"" + class + "\""
  109. }
  110. })
  111. end_backlink := [][]string{}
  112. for k1, v1 := range backlink {
  113. for k2, v2 := range v1 {
  114. end_backlink = append(end_backlink, []string{
  115. data["doc_name"],
  116. k1,
  117. k2,
  118. v2,
  119. })
  120. }
  121. }
  122. end_data := make(map[string]interface{})
  123. end_data["data"] = string_data
  124. end_data["js_data"] = "opennamu_do_toc();"
  125. end_data["backlink"] = end_backlink
  126. end_data["link_count"] = link_count
  127. return end_data
  128. }