You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

markdown.go 3.9 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package markdown
  6. import (
  7. "bytes"
  8. "sync"
  9. "code.gitea.io/gitea/modules/log"
  10. "code.gitea.io/gitea/modules/markup"
  11. "code.gitea.io/gitea/modules/markup/common"
  12. "code.gitea.io/gitea/modules/setting"
  13. giteautil "code.gitea.io/gitea/modules/util"
  14. "github.com/yuin/goldmark"
  15. meta "github.com/yuin/goldmark-meta"
  16. "github.com/yuin/goldmark/extension"
  17. "github.com/yuin/goldmark/parser"
  18. "github.com/yuin/goldmark/renderer"
  19. "github.com/yuin/goldmark/renderer/html"
  20. "github.com/yuin/goldmark/util"
  21. )
  22. var converter goldmark.Markdown
  23. var once = sync.Once{}
  24. var urlPrefixKey = parser.NewContextKey()
  25. var isWikiKey = parser.NewContextKey()
  26. // NewGiteaParseContext creates a parser.Context with the gitea context set
  27. func NewGiteaParseContext(urlPrefix string, isWiki bool) parser.Context {
  28. pc := parser.NewContext(parser.WithIDs(newPrefixedIDs()))
  29. pc.Set(urlPrefixKey, urlPrefix)
  30. pc.Set(isWikiKey, isWiki)
  31. return pc
  32. }
  33. // RenderRaw renders Markdown to HTML without handling special links.
  34. func RenderRaw(body []byte, urlPrefix string, wikiMarkdown bool) []byte {
  35. once.Do(func() {
  36. converter = goldmark.New(
  37. goldmark.WithExtensions(extension.Table,
  38. extension.Strikethrough,
  39. extension.TaskList,
  40. extension.DefinitionList,
  41. common.FootnoteExtension,
  42. extension.NewTypographer(
  43. extension.WithTypographicSubstitutions(extension.TypographicSubstitutions{
  44. extension.EnDash: nil,
  45. extension.EmDash: nil,
  46. extension.Ellipsis: nil,
  47. }),
  48. ),
  49. meta.Meta,
  50. ),
  51. goldmark.WithParserOptions(
  52. parser.WithAttribute(),
  53. parser.WithAutoHeadingID(),
  54. parser.WithASTTransformers(
  55. util.Prioritized(&ASTTransformer{}, 10000),
  56. ),
  57. ),
  58. goldmark.WithRendererOptions(
  59. html.WithUnsafe(),
  60. ),
  61. )
  62. // Override the original Tasklist renderer!
  63. converter.Renderer().AddOptions(
  64. renderer.WithNodeRenderers(
  65. util.Prioritized(NewHTMLRenderer(), 10),
  66. ),
  67. )
  68. if setting.Markdown.EnableHardLineBreak {
  69. converter.Renderer().AddOptions(html.WithHardWraps())
  70. }
  71. })
  72. pc := NewGiteaParseContext(urlPrefix, wikiMarkdown)
  73. var buf bytes.Buffer
  74. if err := converter.Convert(giteautil.NormalizeEOL(body), &buf, parser.WithContext(pc)); err != nil {
  75. log.Error("Unable to render: %v", err)
  76. }
  77. return markup.SanitizeReader(&buf).Bytes()
  78. }
  79. var (
  80. // MarkupName describes markup's name
  81. MarkupName = "markdown"
  82. )
  83. func init() {
  84. markup.RegisterParser(Parser{})
  85. }
  86. // Parser implements markup.Parser
  87. type Parser struct{}
  88. // Name implements markup.Parser
  89. func (Parser) Name() string {
  90. return MarkupName
  91. }
  92. // Extensions implements markup.Parser
  93. func (Parser) Extensions() []string {
  94. return setting.Markdown.FileExtensions
  95. }
  96. // Render implements markup.Parser
  97. func (Parser) Render(rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte {
  98. return RenderRaw(rawBytes, urlPrefix, isWiki)
  99. }
  100. // Render renders Markdown to HTML with all specific handling stuff.
  101. func Render(rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
  102. return markup.Render("a.md", rawBytes, urlPrefix, metas)
  103. }
  104. // RenderString renders Markdown to HTML with special links and returns string type.
  105. func RenderString(raw, urlPrefix string, metas map[string]string) string {
  106. return markup.RenderString("a.md", raw, urlPrefix, metas)
  107. }
  108. // RenderWiki renders markdown wiki page to HTML and return HTML string
  109. func RenderWiki(rawBytes []byte, urlPrefix string, metas map[string]string) string {
  110. return markup.RenderWiki("a.md", rawBytes, urlPrefix, metas)
  111. }
  112. // IsMarkdownFile reports whether name looks like a Markdown file
  113. // based on its extension.
  114. func IsMarkdownFile(name string) bool {
  115. return markup.IsMarkupFile(name, MarkupName)
  116. }