mirror of
https://github.com/kovetskiy/mark.git
synced 2025-04-24 05:42:40 +08:00
143 lines
3.4 KiB
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
|
|
}
|