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