mark/pkg/mark/markdown.go

143 lines
3.4 KiB
Go

package mark
import (
"bytes"
"regexp"
"strconv"
"strings"
"github.com/kovetskiy/mark/pkg/mark/stdlib"
"github.com/reconquest/pkg/log"
"github.com/russross/blackfriday"
)
type ConfluenceRenderer struct {
blackfriday.Renderer
Stdlib *stdlib.Lib
}
func ParseLanguage(lang string) string {
// lang takes the following form: language? "collapse"? ("title"? <any string>*)?
// let's split it by spaces
paramlist := strings.Fields(lang)
// get the word in question, aka the first one
first := lang
if len(paramlist) > 0 {
first = paramlist[0]
}
if first == "collapse" || first == "title" {
// collapsing or including a title without a language
return ""
}
// the default case with language being the first one
return first
}
func ParseTitle(lang string) string {
index := strings.Index(lang, "title")
if index >= 0 {
// it's found, check if title is given and return it
start := index + 6
if len(lang) > start {
return lang[start:]
}
}
return ""
}
func (renderer ConfluenceRenderer) BlockCode(
out *bytes.Buffer,
text []byte,
lang string,
) {
renderer.Stdlib.Templates.ExecuteTemplate(
out,
"ac:code",
struct {
Language string
Collapse string
Title string
Text string
}{
ParseLanguage(lang),
strconv.FormatBool(strings.Contains(lang, "collapse")),
ParseTitle(lang),
string(text),
},
)
}
// compileMarkdown will replace tags like <ac:rich-tech-body> with escaped
// equivalent, because blackfriday markdown parser replaces that tags with
// <a href="ac:rich-text-body">ac:rich-text-body</a> for whatever reason.
func CompileMarkdown(
markdown []byte,
stdlib *stdlib.Lib,
) string {
log.Tracef(nil, "rendering markdown:\n%s", string(markdown))
colon := regexp.MustCompile(`---BLACKFRIDAY-COLON---`)
tags := regexp.MustCompile(`<(/?\S+?):(\S+?)>`)
markdown = tags.ReplaceAll(
markdown,
[]byte(`<$1`+colon.String()+`$2>`),
)
renderer := ConfluenceRenderer{
Renderer: blackfriday.HtmlRenderer(
blackfriday.HTML_USE_XHTML|
blackfriday.HTML_USE_SMARTYPANTS|
blackfriday.HTML_SMARTYPANTS_FRACTIONS|
blackfriday.HTML_SMARTYPANTS_DASHES|
blackfriday.HTML_SMARTYPANTS_LATEX_DASHES,
"", "",
),
Stdlib: stdlib,
}
html := blackfriday.MarkdownOptions(
markdown,
renderer,
blackfriday.Options{
Extensions: blackfriday.EXTENSION_NO_INTRA_EMPHASIS |
blackfriday.EXTENSION_TABLES |
blackfriday.EXTENSION_FENCED_CODE |
blackfriday.EXTENSION_AUTOLINK |
blackfriday.EXTENSION_LAX_HTML_BLOCKS |
blackfriday.EXTENSION_STRIKETHROUGH |
blackfriday.EXTENSION_SPACE_HEADERS |
blackfriday.EXTENSION_HEADER_IDS |
blackfriday.EXTENSION_AUTO_HEADER_IDS |
blackfriday.EXTENSION_TITLEBLOCK |
blackfriday.EXTENSION_BACKSLASH_LINE_BREAK |
blackfriday.EXTENSION_DEFINITION_LISTS |
blackfriday.EXTENSION_JOIN_LINES |
blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK,
},
)
html = colon.ReplaceAll(html, []byte(`:`))
log.Tracef(nil, "rendered markdown to html:\n%s", string(html))
return string(html)
}
// DropDocumentLeadingH1 will drop leading H1 headings to prevent
// duplication of or visual conflict with page titles.
// NOTE: This is intended only to operate on the whole markdown document.
// Operating on individual lines will clear them if the begin with `#`.
func DropDocumentLeadingH1(
markdown []byte,
) []byte {
h1 := regexp.MustCompile(`^#[^#].*\n`)
markdown = h1.ReplaceAll(markdown, []byte(""))
return markdown
}