mirror of
https://github.com/danog/blackfriday.git
synced 2025-01-22 21:31:20 +01:00
setup, starting reference handling
This commit is contained in:
parent
cb7b546677
commit
c727274128
388
markdown.go
388
markdown.go
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
|
"sort"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -76,24 +77,194 @@ type mkd_renderer struct {
|
|||||||
table_row func(ob *bytes.Buffer, text []byte, opaque interface{})
|
table_row func(ob *bytes.Buffer, text []byte, opaque interface{})
|
||||||
table_cell func(ob *bytes.Buffer, text []byte, flags int, opaque interface{})
|
table_cell func(ob *bytes.Buffer, text []byte, flags int, opaque interface{})
|
||||||
|
|
||||||
|
// span-level callbacks---nil or return 0 prints the span verbatim
|
||||||
|
autolink func(ob *bytes.Buffer, link []byte, kind int, opaque interface{})
|
||||||
|
codespan func(ob *bytes.Buffer, text []byte, opaque interface{})
|
||||||
|
double_emphasis func(ob *bytes.Buffer, text []byte, opaque interface{})
|
||||||
|
emphasis func(ob *bytes.Buffer, text []byte, opaque interface{})
|
||||||
|
image func(ob *bytes.Buffer, link []byte, title []byte, alt []byte, opaque interface{})
|
||||||
|
linebreak func(ob *bytes.Buffer, opaque interface{})
|
||||||
|
link func(ob *bytes.Buffer, link []byte, title []byte, content []byte, opaque interface{})
|
||||||
|
raw_html_tag func(ob *bytes.Buffer, tag []byte, opaque interface{})
|
||||||
|
triple_emphasis func(ob *bytes.Buffer, text []byte, opaque interface{})
|
||||||
|
strikethrough func(ob *bytes.Buffer, text []byte, opaque interface{})
|
||||||
|
|
||||||
|
// low-level callbacks---nil copies input directly into the output
|
||||||
|
entity func(ob *bytes.Buffer, entity []byte, opaque interface{})
|
||||||
|
normal_text func(ob *bytes.Buffer, text []byte, opaque interface{})
|
||||||
|
|
||||||
|
// header and footer
|
||||||
|
doc_header func(ob *bytes.Buffer, opaque interface{})
|
||||||
|
doc_footer func(ob *bytes.Buffer, opaque interface{})
|
||||||
|
|
||||||
// user data---passed back to every callback
|
// user data---passed back to every callback
|
||||||
opaque interface{}
|
opaque interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type render struct {
|
type link_ref struct {
|
||||||
mk mkd_renderer
|
id []byte
|
||||||
ext_flags uint32
|
link []byte
|
||||||
// ...
|
title []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse_inline(work *bytes.Buffer, rndr *render, data []byte) {
|
type link_ref_array []*link_ref
|
||||||
// TODO: inline rendering
|
|
||||||
work.Write(data)
|
// implement the sorting interface
|
||||||
|
func (elt link_ref_array) Len() int {
|
||||||
|
return len(elt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (elt link_ref_array) Less(i, j int) bool {
|
||||||
|
a, b := elt[i].id, elt[j].id
|
||||||
|
|
||||||
|
// adapted from bytes.Compare in stdlib
|
||||||
|
m := len(a)
|
||||||
|
if m > len(b) {
|
||||||
|
m = len(b)
|
||||||
|
}
|
||||||
|
for i, ac := range a[0:m] {
|
||||||
|
// do a case-insensitive comparison
|
||||||
|
ai, bi := unicode.ToLower(int(ac)), unicode.ToLower(int(b[i]))
|
||||||
|
switch {
|
||||||
|
case ai > bi:
|
||||||
|
return false
|
||||||
|
case ai < bi:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case len(a) < len(b):
|
||||||
|
return true
|
||||||
|
case len(a) > len(b):
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (elt link_ref_array) Swap(i, j int) {
|
||||||
|
elt[i], elt[j] = elt[j], elt[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_ref(data []byte, beg int, last *int, rndr *render) bool {
|
||||||
|
// TODO: stopped here
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type render struct {
|
||||||
|
mk *mkd_renderer
|
||||||
|
refs link_ref_array
|
||||||
|
active_char [256]int
|
||||||
|
ext_flags uint32
|
||||||
|
nesting int
|
||||||
|
max_nesting int
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
MD_CHAR_NONE = iota
|
||||||
|
MD_CHAR_EMPHASIS
|
||||||
|
MD_CHAR_CODESPAN
|
||||||
|
MD_CHAR_LINEBREAK
|
||||||
|
MD_CHAR_LINK
|
||||||
|
MD_CHAR_LANGLE
|
||||||
|
MD_CHAR_ESCAPE
|
||||||
|
MD_CHAR_ENTITITY
|
||||||
|
MD_CHAR_AUTOLINK
|
||||||
|
)
|
||||||
|
|
||||||
|
// closures to render active chars, each:
|
||||||
|
// returns the number of chars taken care of
|
||||||
|
// data is the complete block being rendered
|
||||||
|
// offset is the number of valid chars before the data
|
||||||
|
var markdown_char_ptrs = [...]func(ob *bytes.Buffer, rndr *render, data []byte, offset int) int{
|
||||||
|
nil,
|
||||||
|
char_emphasis,
|
||||||
|
char_codespan,
|
||||||
|
char_linebreak,
|
||||||
|
char_link,
|
||||||
|
char_langle_tag,
|
||||||
|
char_escape,
|
||||||
|
char_entity,
|
||||||
|
char_autolink,
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_inline(ob *bytes.Buffer, rndr *render, data []byte) {
|
||||||
|
if rndr.nesting >= rndr.max_nesting {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rndr.nesting++
|
||||||
|
|
||||||
|
i, end := 0, 0
|
||||||
|
for i < len(data) {
|
||||||
|
// copy inactive chars into the output
|
||||||
|
for end < len(data) && rndr.active_char[data[end]] == 0 {
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
|
||||||
|
if rndr.mk.normal_text != nil {
|
||||||
|
rndr.mk.normal_text(ob, data[i:], rndr.mk.opaque)
|
||||||
|
} else {
|
||||||
|
ob.Write(data[i:])
|
||||||
|
}
|
||||||
|
|
||||||
|
if end >= len(data) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i = end
|
||||||
|
|
||||||
|
// call the trigger
|
||||||
|
action := rndr.active_char[data[end]]
|
||||||
|
end = markdown_char_ptrs[action](ob, rndr, data, i)
|
||||||
|
|
||||||
|
if end == 0 { // no action from the callback
|
||||||
|
end = i + 1
|
||||||
|
} else {
|
||||||
|
i += end
|
||||||
|
end = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rndr.nesting--
|
||||||
|
}
|
||||||
|
|
||||||
|
func char_emphasis(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func char_codespan(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func char_linebreak(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func char_link(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func char_langle_tag(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func char_escape(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func char_entity(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func char_autolink(ob *bytes.Buffer, rndr *render, data []byte, offset int) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// parse block-level data
|
// parse block-level data
|
||||||
func parse_block(ob *bytes.Buffer, rndr *render, data []byte) {
|
func parse_block(ob *bytes.Buffer, rndr *render, data []byte) {
|
||||||
// TODO: quit if max_nesting exceeded
|
if rndr.nesting >= rndr.max_nesting {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rndr.nesting++
|
||||||
|
|
||||||
for len(data) > 0 {
|
for len(data) > 0 {
|
||||||
if is_atxheader(rndr, data) {
|
if is_atxheader(rndr, data) {
|
||||||
@ -151,6 +322,8 @@ func parse_block(ob *bytes.Buffer, rndr *render, data []byte) {
|
|||||||
|
|
||||||
data = data[parse_paragraph(ob, rndr, data):]
|
data = data[parse_paragraph(ob, rndr, data):]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rndr.nesting--
|
||||||
}
|
}
|
||||||
|
|
||||||
func is_atxheader(rndr *render, data []byte) bool {
|
func is_atxheader(rndr *render, data []byte) bool {
|
||||||
@ -234,13 +407,13 @@ func is_headerline(data []byte) int {
|
|||||||
func parse_htmlblock(ob *bytes.Buffer, rndr *render, data []byte, do_render bool) int {
|
func parse_htmlblock(ob *bytes.Buffer, rndr *render, data []byte, do_render bool) int {
|
||||||
var i, j int
|
var i, j int
|
||||||
|
|
||||||
// identification of the opening tag
|
// identify the opening tag
|
||||||
if len(data) < 2 || data[0] != '<' {
|
if len(data) < 2 || data[0] != '<' {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
curtag, tagfound := find_block_tag(data[1:])
|
curtag, tagfound := find_block_tag(data[1:])
|
||||||
|
|
||||||
// handling of special cases
|
// handle special cases
|
||||||
if !tagfound {
|
if !tagfound {
|
||||||
|
|
||||||
// HTML comment, laxist form
|
// HTML comment, laxist form
|
||||||
@ -289,12 +462,12 @@ func parse_htmlblock(ob *bytes.Buffer, rndr *render, data []byte, do_render bool
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// looking for an unindented matching closing tag
|
// look for an unindented matching closing tag
|
||||||
// followed by a blank line
|
// followed by a blank line
|
||||||
i = 1
|
i = 1
|
||||||
found := false
|
found := false
|
||||||
|
|
||||||
// if not found, trying a second pass looking for indented match
|
// if not found, try a second pass looking for indented match
|
||||||
// but not if tag is "ins" or "del" (following original Markdown.pl)
|
// but not if tag is "ins" or "del" (following original Markdown.pl)
|
||||||
if curtag != "ins" && curtag != "del" {
|
if curtag != "ins" && curtag != "del" {
|
||||||
i = 1
|
i = 1
|
||||||
@ -346,14 +519,14 @@ func find_block_tag(data []byte) (string, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func htmlblock_end(tag string, rndr *render, data []byte) int {
|
func htmlblock_end(tag string, rndr *render, data []byte) int {
|
||||||
// assuming data[0] == '<' && data[1] == '/' already tested
|
// assume data[0] == '<' && data[1] == '/' already tested
|
||||||
|
|
||||||
// checking tag is a match
|
// check if tag is a match
|
||||||
if len(tag)+3 >= len(data) || bytes.Compare(data[2:2+len(tag)], []byte(tag)) != 0 || data[len(tag)+2] != '>' {
|
if len(tag)+3 >= len(data) || bytes.Compare(data[2:2+len(tag)], []byte(tag)) != 0 || data[len(tag)+2] != '>' {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// checking white lines
|
// check white lines
|
||||||
i := len(tag) + 3
|
i := len(tag) + 3
|
||||||
w := 0
|
w := 0
|
||||||
if i < len(data) {
|
if i < len(data) {
|
||||||
@ -390,7 +563,7 @@ func is_empty(data []byte) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func is_hrule(data []byte) bool {
|
func is_hrule(data []byte) bool {
|
||||||
// skipping initial spaces
|
// skip initial spaces
|
||||||
if len(data) < 3 {
|
if len(data) < 3 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -405,7 +578,7 @@ func is_hrule(data []byte) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// looking at the hrule char
|
// look at the hrule char
|
||||||
if i+2 >= len(data) || (data[i] != '*' && data[i] != '-' && data[i] != '_') {
|
if i+2 >= len(data) || (data[i] != '*' && data[i] != '-' && data[i] != '_') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -429,7 +602,7 @@ func is_hrule(data []byte) bool {
|
|||||||
func is_codefence(data []byte, syntax **string) int {
|
func is_codefence(data []byte, syntax **string) int {
|
||||||
i, n := 0, 0
|
i, n := 0, 0
|
||||||
|
|
||||||
// skipping initial spaces
|
// skip initial spaces
|
||||||
if len(data) < 3 {
|
if len(data) < 3 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -443,7 +616,7 @@ func is_codefence(data []byte, syntax **string) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// looking at the hrule char
|
// look at the hrule char
|
||||||
if i+2 >= len(data) || !(data[i] == '~' || data[i] == '`') {
|
if i+2 >= len(data) || !(data[i] == '~' || data[i] == '`') {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -759,7 +932,7 @@ func parse_blockquote(ob *bytes.Buffer, rndr *render, data []byte) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pre := prefix_quote(data[beg:]); pre > 0 {
|
if pre := prefix_quote(data[beg:]); pre > 0 {
|
||||||
beg += pre // skipping prefix
|
beg += pre // skip prefix
|
||||||
} else {
|
} else {
|
||||||
// empty line followed by non-quote line
|
// empty line followed by non-quote line
|
||||||
if is_empty(data[beg:]) > 0 && (end >= len(data) || (prefix_quote(data[end:]) == 0 && is_empty(data[end:]) == 0)) {
|
if is_empty(data[beg:]) > 0 && (end >= len(data) || (prefix_quote(data[end:]) == 0 && is_empty(data[end:]) == 0)) {
|
||||||
@ -875,7 +1048,7 @@ func parse_list(ob *bytes.Buffer, rndr *render, data []byte, flags int) int {
|
|||||||
|
|
||||||
i, j := 0, 0
|
i, j := 0, 0
|
||||||
for i < len(data) {
|
for i < len(data) {
|
||||||
j, flags = parse_listitem(work, rndr, data[i:], flags)
|
j = parse_listitem(work, rndr, data[i:], &flags)
|
||||||
i += j
|
i += j
|
||||||
|
|
||||||
if j == 0 || flags&MKD_LI_END != 0 {
|
if j == 0 || flags&MKD_LI_END != 0 {
|
||||||
@ -889,12 +1062,10 @@ func parse_list(ob *bytes.Buffer, rndr *render, data []byte, flags int) int {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
// parsing a single list item
|
// parse a single list item
|
||||||
// assuming initial prefix is already removed
|
// assumes initial prefix is already removed
|
||||||
func parse_listitem(ob *bytes.Buffer, rndr *render, data []byte, flags_in int) (size int, flags int) {
|
func parse_listitem(ob *bytes.Buffer, rndr *render, data []byte, flags *int) int {
|
||||||
size, flags = 0, flags_in
|
// keep track of the first indentation prefix
|
||||||
|
|
||||||
// keeping book of the first indentation prefix
|
|
||||||
beg, end, pre, sublist, orgpre, i := 0, 0, 0, 0, 0, 0
|
beg, end, pre, sublist, orgpre, i := 0, 0, 0, 0, 0, 0
|
||||||
|
|
||||||
for orgpre < 3 && orgpre < len(data) && data[orgpre] == ' ' {
|
for orgpre < 3 && orgpre < len(data) && data[orgpre] == ' ' {
|
||||||
@ -906,20 +1077,20 @@ func parse_listitem(ob *bytes.Buffer, rndr *render, data []byte, flags_in int) (
|
|||||||
beg = prefix_oli(data)
|
beg = prefix_oli(data)
|
||||||
}
|
}
|
||||||
if beg == 0 {
|
if beg == 0 {
|
||||||
return
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// skipping to the beginning of the following line
|
// skip to the beginning of the following line
|
||||||
end = beg
|
end = beg
|
||||||
for end < len(data) && data[end-1] != '\n' {
|
for end < len(data) && data[end-1] != '\n' {
|
||||||
end++
|
end++
|
||||||
}
|
}
|
||||||
|
|
||||||
// getting working buffers
|
// get working buffers
|
||||||
work := bytes.NewBuffer(nil)
|
work := bytes.NewBuffer(nil)
|
||||||
inter := bytes.NewBuffer(nil)
|
inter := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
// putting the first line into the working buffer
|
// put the first line into the working buffer
|
||||||
work.Write(data[beg:end])
|
work.Write(data[beg:end])
|
||||||
beg = end
|
beg = end
|
||||||
|
|
||||||
@ -939,7 +1110,7 @@ func parse_listitem(ob *bytes.Buffer, rndr *render, data []byte, flags_in int) (
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculating the indentation
|
// calculate the indentation
|
||||||
i = 0
|
i = 0
|
||||||
for i < 4 && beg+i < end && data[beg+i] == ' ' {
|
for i < 4 && beg+i < end && data[beg+i] == ' ' {
|
||||||
i++
|
i++
|
||||||
@ -951,24 +1122,24 @@ func parse_listitem(ob *bytes.Buffer, rndr *render, data []byte, flags_in int) (
|
|||||||
pre = 8
|
pre = 8
|
||||||
}
|
}
|
||||||
|
|
||||||
// checking for a new item
|
// check for a new item
|
||||||
chunk := data[beg+i : end]
|
chunk := data[beg+i : end]
|
||||||
if (prefix_uli(chunk) > 0 && !is_hrule(chunk)) || prefix_oli(chunk) > 0 {
|
if (prefix_uli(chunk) > 0 && !is_hrule(chunk)) || prefix_oli(chunk) > 0 {
|
||||||
if in_empty {
|
if in_empty {
|
||||||
has_inside_empty = true
|
has_inside_empty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if pre == orgpre { // the following item must have
|
if pre == orgpre { // the following item must have the same indentation
|
||||||
break // the same indentation
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if sublist == 0 {
|
if sublist == 0 {
|
||||||
sublist = work.Len()
|
sublist = work.Len()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// joining only indented stuff after empty lines
|
// only join indented stuff after empty lines
|
||||||
if in_empty && i < 4 && data[beg] != '\t' {
|
if in_empty && i < 4 && data[beg] != '\t' {
|
||||||
flags |= MKD_LI_END
|
*flags |= MKD_LI_END
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
if in_empty {
|
if in_empty {
|
||||||
@ -980,18 +1151,18 @@ func parse_listitem(ob *bytes.Buffer, rndr *render, data []byte, flags_in int) (
|
|||||||
|
|
||||||
in_empty = false
|
in_empty = false
|
||||||
|
|
||||||
// adding the line without prefix into the working buffer
|
// add the line into the working buffer without prefix
|
||||||
work.Write(data[beg+i : end])
|
work.Write(data[beg+i : end])
|
||||||
beg = end
|
beg = end
|
||||||
}
|
}
|
||||||
|
|
||||||
// render of li contents
|
// render li contents
|
||||||
if has_inside_empty {
|
if has_inside_empty {
|
||||||
flags |= MKD_LI_BLOCK
|
*flags |= MKD_LI_BLOCK
|
||||||
}
|
}
|
||||||
|
|
||||||
workbytes := work.Bytes()
|
workbytes := work.Bytes()
|
||||||
if flags&MKD_LI_BLOCK != 0 {
|
if *flags&MKD_LI_BLOCK != 0 {
|
||||||
// intermediate render of block li
|
// intermediate render of block li
|
||||||
if sublist > 0 && sublist < len(workbytes) {
|
if sublist > 0 && sublist < len(workbytes) {
|
||||||
parse_block(inter, rndr, workbytes[:sublist])
|
parse_block(inter, rndr, workbytes[:sublist])
|
||||||
@ -1009,13 +1180,12 @@ func parse_listitem(ob *bytes.Buffer, rndr *render, data []byte, flags_in int) (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// render of li itself
|
// render li itself
|
||||||
if rndr.mk.listitem != nil {
|
if rndr.mk.listitem != nil {
|
||||||
rndr.mk.listitem(ob, inter.Bytes(), flags, rndr.mk.opaque)
|
rndr.mk.listitem(ob, inter.Bytes(), *flags, rndr.mk.opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
size = beg
|
return beg
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse_paragraph(ob *bytes.Buffer, rndr *render, data []byte) int {
|
func parse_paragraph(ob *bytes.Buffer, rndr *render, data []byte) int {
|
||||||
@ -1416,18 +1586,136 @@ func main() {
|
|||||||
rndrer.table_cell = rndr_tablecell
|
rndrer.table_cell = rndr_tablecell
|
||||||
rndrer.opaque = &html_renderopts{close_tag: " />"}
|
rndrer.opaque = &html_renderopts{close_tag: " />"}
|
||||||
var extensions uint32 = MKDEXT_FENCED_CODE | MKDEXT_TABLES
|
var extensions uint32 = MKDEXT_FENCED_CODE | MKDEXT_TABLES
|
||||||
Ups_markdown(ob, ib, rndrer, extensions)
|
Markdown(ob, ib, rndrer, extensions)
|
||||||
fmt.Print(ob.String())
|
fmt.Print(ob.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func Ups_markdown(ob *bytes.Buffer, ib []byte, rndrer *mkd_renderer, extensions uint32) {
|
func expand_tabs(ob *bytes.Buffer, line []byte) {
|
||||||
|
i, tab := 0, 0
|
||||||
|
|
||||||
/* filling the render structure */
|
for i < len(line) {
|
||||||
|
org := i
|
||||||
|
for i < len(line) && line[i] != '\t' {
|
||||||
|
i++
|
||||||
|
tab++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > org {
|
||||||
|
ob.Write(line[org:i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= len(line) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
ob.WriteByte(' ')
|
||||||
|
tab++
|
||||||
|
if tab%4 == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Markdown(ob *bytes.Buffer, ib []byte, rndrer *mkd_renderer, extensions uint32) {
|
||||||
|
// no point in parsing if we can't render
|
||||||
if rndrer == nil {
|
if rndrer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rndr := &render{*rndrer, extensions}
|
// fill in the render structure
|
||||||
|
rndr := new(render)
|
||||||
|
rndr.mk = rndrer
|
||||||
|
rndr.ext_flags = extensions
|
||||||
|
rndr.max_nesting = 16
|
||||||
|
|
||||||
parse_block(ob, rndr, ib)
|
if rndr.mk.emphasis != nil || rndr.mk.double_emphasis != nil || rndr.mk.triple_emphasis != nil {
|
||||||
|
rndr.active_char['*'] = MD_CHAR_EMPHASIS
|
||||||
|
rndr.active_char['_'] = MD_CHAR_EMPHASIS
|
||||||
|
if extensions&MKDEXT_STRIKETHROUGH != 0 {
|
||||||
|
rndr.active_char['~'] = MD_CHAR_EMPHASIS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rndr.mk.codespan != nil {
|
||||||
|
rndr.active_char['`'] = MD_CHAR_CODESPAN
|
||||||
|
}
|
||||||
|
if rndr.mk.linebreak != nil {
|
||||||
|
rndr.active_char['\n'] = MD_CHAR_LINEBREAK
|
||||||
|
}
|
||||||
|
if rndr.mk.image != nil || rndr.mk.link != nil {
|
||||||
|
rndr.active_char['['] = MD_CHAR_LINK
|
||||||
|
}
|
||||||
|
rndr.active_char['<'] = MD_CHAR_LANGLE
|
||||||
|
rndr.active_char['\\'] = MD_CHAR_ESCAPE
|
||||||
|
rndr.active_char['&'] = MD_CHAR_ENTITITY
|
||||||
|
|
||||||
|
if extensions&MKDEXT_AUTOLINK != 0 {
|
||||||
|
rndr.active_char['h'] = MD_CHAR_AUTOLINK // http, https
|
||||||
|
rndr.active_char['H'] = MD_CHAR_AUTOLINK
|
||||||
|
|
||||||
|
rndr.active_char['f'] = MD_CHAR_AUTOLINK // ftp
|
||||||
|
rndr.active_char['F'] = MD_CHAR_AUTOLINK
|
||||||
|
|
||||||
|
rndr.active_char['m'] = MD_CHAR_AUTOLINK // mailto
|
||||||
|
rndr.active_char['M'] = MD_CHAR_AUTOLINK
|
||||||
|
}
|
||||||
|
|
||||||
|
// first pass: look for references, copying everything else
|
||||||
|
text := bytes.NewBuffer(make([]byte, len(ib)))
|
||||||
|
beg, end := 0, 0
|
||||||
|
for beg < len(ib) { // iterate over lines
|
||||||
|
if is_ref(ib, beg, &end, rndr) {
|
||||||
|
beg = end
|
||||||
|
} else { // skip to the next line
|
||||||
|
end = beg
|
||||||
|
for end < len(ib) && ib[end] != '\n' && ib[end] != '\r' {
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the line body if present
|
||||||
|
if end > beg {
|
||||||
|
expand_tabs(text, ib[beg:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
for end < len(ib) && (ib[end] == '\n' || ib[end] == '\r') {
|
||||||
|
// add one \n per newline
|
||||||
|
if ib[end] == '\n' || (end+1 < len(ib) && ib[end+1] != '\n') {
|
||||||
|
text.WriteByte('\n')
|
||||||
|
}
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
|
||||||
|
beg = end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the reference array
|
||||||
|
if len(rndr.refs) > 1 {
|
||||||
|
sort.Sort(rndr.refs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// second pass: actual rendering
|
||||||
|
if rndr.mk.doc_header != nil {
|
||||||
|
rndr.mk.doc_header(ob, rndr.mk.opaque)
|
||||||
|
}
|
||||||
|
|
||||||
|
if text.Len() > 0 {
|
||||||
|
// add a final newline if not already present
|
||||||
|
finalchar := text.Bytes()[text.Len()-1]
|
||||||
|
if finalchar != '\n' && finalchar != '\r' {
|
||||||
|
text.WriteByte('\n')
|
||||||
|
}
|
||||||
|
parse_block(ob, rndr, text.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
if rndr.mk.doc_footer != nil {
|
||||||
|
rndr.mk.doc_footer(ob, rndr.mk.opaque)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rndr.nesting != 0 {
|
||||||
|
panic("Nesting level did not end at zero")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user