2014-04-10 14:20:58 -04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2016-02-20 17:10:05 -05:00
package markdown
2014-04-10 14:20:58 -04:00
import (
"bytes"
"strings"
2017-04-21 15:01:08 +08:00
"code.gitea.io/gitea/modules/markup"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/setting"
2018-02-20 04:50:42 -08:00
"code.gitea.io/gitea/modules/util"
2017-07-06 16:38:38 -04:00
"github.com/russross/blackfriday"
2014-04-10 14:20:58 -04:00
)
2016-02-20 17:10:05 -05:00
// Renderer is a extended version of underlying render object.
type Renderer struct {
2014-10-04 17:15:22 -04:00
blackfriday . Renderer
2017-09-21 13:20:14 +08:00
URLPrefix string
IsWiki bool
2014-04-10 14:20:58 -04:00
}
2016-02-20 17:10:05 -05:00
// Link defines how formal links should be processed to produce corresponding HTML elements.
func ( r * Renderer ) Link ( out * bytes . Buffer , link [ ] byte , title [ ] byte , content [ ] byte ) {
2017-09-17 01:17:57 +08:00
if len ( link ) > 0 && ! markup . IsLink ( link ) {
2016-02-20 17:10:05 -05:00
if link [ 0 ] != '#' {
2017-02-24 21:59:56 +07:00
lnk := string ( link )
2017-09-21 13:20:14 +08:00
if r . IsWiki {
2018-02-20 04:50:42 -08:00
lnk = util . URLJoin ( "wiki" , lnk )
2017-02-14 08:13:59 +07:00
}
2018-02-20 04:50:42 -08:00
mLink := util . URLJoin ( r . URLPrefix , lnk )
2017-02-14 08:13:59 +07:00
link = [ ] byte ( mLink )
2016-01-09 10:59:04 +08:00
}
}
r . Renderer . Link ( out , link , title , content )
}
2017-02-14 08:13:59 +07:00
// List renders markdown bullet or digit lists to HTML
func ( r * Renderer ) List ( out * bytes . Buffer , text func ( ) bool , flags int ) {
marker := out . Len ( )
if out . Len ( ) > 0 {
out . WriteByte ( '\n' )
2016-01-09 10:59:04 +08:00
}
2017-02-14 08:13:59 +07:00
if flags & blackfriday . LIST_TYPE_DEFINITION != 0 {
out . WriteString ( "<dl>" )
} else if flags & blackfriday . LIST_TYPE_ORDERED != 0 {
out . WriteString ( "<ol class='ui list'>" )
} else {
out . WriteString ( "<ul class='ui list'>" )
}
if ! text ( ) {
out . Truncate ( marker )
return
}
if flags & blackfriday . LIST_TYPE_DEFINITION != 0 {
out . WriteString ( "</dl>\n" )
} else if flags & blackfriday . LIST_TYPE_ORDERED != 0 {
out . WriteString ( "</ol>\n" )
} else {
out . WriteString ( "</ul>\n" )
2014-04-10 14:20:58 -04:00
}
}
2016-02-20 17:10:05 -05:00
// ListItem defines how list items should be processed to produce corresponding HTML elements.
2016-11-25 09:58:05 +08:00
func ( r * Renderer ) ListItem ( out * bytes . Buffer , text [ ] byte , flags int ) {
2016-02-20 17:10:05 -05:00
// Detect procedures to draw checkboxes.
2017-02-14 08:13:59 +07:00
prefix := ""
if bytes . HasPrefix ( text , [ ] byte ( "<p>" ) ) {
prefix = "<p>"
}
2016-01-13 13:25:52 +01:00
switch {
2017-02-14 08:13:59 +07:00
case bytes . HasPrefix ( text , [ ] byte ( prefix + "[ ] " ) ) :
2017-05-12 03:52:45 -04:00
text = append ( [ ] byte ( ` <span class="ui fitted disabled checkbox"><input type="checkbox" disabled="disabled" /><label /></span> ` ) , text [ 3 + len ( prefix ) : ] ... )
2017-04-24 06:18:36 +02:00
if prefix != "" {
text = bytes . Replace ( text , [ ] byte ( prefix ) , [ ] byte { } , 1 )
}
2017-02-14 08:13:59 +07:00
case bytes . HasPrefix ( text , [ ] byte ( prefix + "[x] " ) ) :
2017-05-12 03:52:45 -04:00
text = append ( [ ] byte ( ` <span class="ui checked fitted disabled checkbox"><input type="checkbox" checked="" disabled="disabled" /><label /></span> ` ) , text [ 3 + len ( prefix ) : ] ... )
2017-04-24 06:18:36 +02:00
if prefix != "" {
text = bytes . Replace ( text , [ ] byte ( prefix ) , [ ] byte { } , 1 )
}
2016-01-13 13:25:52 +01:00
}
2016-11-25 09:58:05 +08:00
r . Renderer . ListItem ( out , text , flags )
2016-01-13 13:25:52 +01:00
}
2016-02-20 17:10:05 -05:00
// Note: this section is for purpose of increase performance and
// reduce memory allocation at runtime since they are constant literals.
2015-11-20 05:37:51 -05:00
var (
svgSuffix = [ ] byte ( ".svg" )
svgSuffixWithMark = [ ] byte ( ".svg?" )
)
2016-02-20 17:10:05 -05:00
// Image defines how images should be processed to produce corresponding HTML elements.
func ( r * Renderer ) Image ( out * bytes . Buffer , link [ ] byte , title [ ] byte , alt [ ] byte ) {
2017-09-21 13:20:14 +08:00
prefix := r . URLPrefix
if r . IsWiki {
2018-02-20 04:50:42 -08:00
prefix = util . URLJoin ( prefix , "wiki" , "src" )
2017-02-14 08:13:59 +07:00
}
prefix = strings . Replace ( prefix , "/src/" , "/raw/" , 1 )
2015-11-20 05:37:51 -05:00
if len ( link ) > 0 {
2017-09-17 01:17:57 +08:00
if markup . IsLink ( link ) {
2015-11-20 05:37:51 -05:00
// External link with .svg suffix usually means CI status.
2016-02-20 17:10:05 -05:00
// TODO: define a keyword to allow non-svg images render as external link.
2015-11-20 05:37:51 -05:00
if bytes . HasSuffix ( link , svgSuffix ) || bytes . Contains ( link , svgSuffixWithMark ) {
2016-01-09 10:59:04 +08:00
r . Renderer . Image ( out , link , title , alt )
2015-11-20 05:37:51 -05:00
return
}
} else {
2017-02-24 21:59:56 +07:00
lnk := string ( link )
2018-02-20 04:50:42 -08:00
lnk = util . URLJoin ( prefix , lnk )
2017-02-24 21:59:56 +07:00
lnk = strings . Replace ( lnk , " " , "+" , - 1 )
link = [ ] byte ( lnk )
2015-11-06 11:10:27 -05:00
}
2014-10-14 23:44:34 -04:00
}
2015-11-06 11:10:27 -05:00
out . WriteString ( ` <a href=" ` )
out . Write ( link )
out . WriteString ( ` "> ` )
2016-01-09 10:59:04 +08:00
r . Renderer . Image ( out , link , title , alt )
2015-11-06 11:10:27 -05:00
out . WriteString ( "</a>" )
2014-10-14 23:44:34 -04:00
}
2016-02-20 17:10:05 -05:00
// RenderRaw renders Markdown to HTML without handling special links.
2017-02-14 08:13:59 +07:00
func RenderRaw ( body [ ] byte , urlPrefix string , wikiMarkdown bool ) [ ] byte {
2014-04-10 14:20:58 -04:00
htmlFlags := 0
2014-10-04 17:15:22 -04:00
htmlFlags |= blackfriday . HTML_SKIP_STYLE
htmlFlags |= blackfriday . HTML_OMIT_CONTENTS
2016-02-20 17:10:05 -05:00
renderer := & Renderer {
2017-09-21 13:20:14 +08:00
Renderer : blackfriday . HtmlRenderer ( htmlFlags , "" , "" ) ,
URLPrefix : urlPrefix ,
IsWiki : wikiMarkdown ,
2014-04-10 14:20:58 -04:00
}
// set up the parser
extensions := 0
2014-10-04 17:15:22 -04:00
extensions |= blackfriday . EXTENSION_NO_INTRA_EMPHASIS
extensions |= blackfriday . EXTENSION_TABLES
extensions |= blackfriday . EXTENSION_FENCED_CODE
extensions |= blackfriday . EXTENSION_STRIKETHROUGH
extensions |= blackfriday . EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
2015-09-01 08:32:02 -04:00
if setting . Markdown . EnableHardLineBreak {
extensions |= blackfriday . EXTENSION_HARD_LINE_BREAK
}
2014-10-04 17:15:22 -04:00
body = blackfriday . Markdown ( body , renderer , extensions )
2014-05-05 13:08:01 -04:00
return body
}
2017-04-21 15:01:08 +08:00
var (
// MarkupName describes markup's name
MarkupName = "markdown"
)
func init ( ) {
markup . RegisterParser ( Parser { } )
}
// Parser implements markup.Parser
type Parser struct {
}
// Name implements markup.Parser
func ( Parser ) Name ( ) string {
return MarkupName
}
// Extensions implements markup.Parser
func ( Parser ) Extensions ( ) [ ] string {
return setting . Markdown . FileExtensions
}
// Render implements markup.Parser
func ( Parser ) Render ( rawBytes [ ] byte , urlPrefix string , metas map [ string ] string , isWiki bool ) [ ] byte {
2017-09-17 01:17:57 +08:00
return RenderRaw ( rawBytes , urlPrefix , isWiki )
}
// Render renders Markdown to HTML with all specific handling stuff.
func Render ( rawBytes [ ] byte , urlPrefix string , metas map [ string ] string ) [ ] byte {
return markup . Render ( "a.md" , rawBytes , urlPrefix , metas )
}
// RenderString renders Markdown to HTML with special links and returns string type.
func RenderString ( raw , urlPrefix string , metas map [ string ] string ) string {
return markup . RenderString ( "a.md" , raw , urlPrefix , metas )
}
// RenderWiki renders markdown wiki page to HTML and return HTML string
func RenderWiki ( rawBytes [ ] byte , urlPrefix string , metas map [ string ] string ) string {
return markup . RenderWiki ( "a.md" , rawBytes , urlPrefix , metas )
}
// IsMarkdownFile reports whether name looks like a Markdown file
// based on its extension.
func IsMarkdownFile ( name string ) bool {
return markup . IsMarkupFile ( name , MarkupName )
2017-04-21 15:01:08 +08:00
}