all repos — grayfriday @ cab328f2f9a83f1518b5cb2b95d0bbb5ee94887b

blackfriday fork with a few changes

cleanup
Russ Ross russ@russross.com
Sat, 28 May 2011 13:00:47 -0600
commit

cab328f2f9a83f1518b5cb2b95d0bbb5ee94887b

parent

b117dcc9eca29c0c1787507bbdb5e3b8b0710dac

1 files changed, 91 insertions(+), 95 deletions(-)

jump to
M markdown.gomarkdown.go

@@ -70,7 +70,7 @@ "blockquote": true,

} // functions for rendering parsed data -type mkd_renderer struct { +type Renderer struct { // block-level callbacks---nil skips the block blockcode func(ob *bytes.Buffer, text []byte, lang string, opaque interface{}) blockquote func(ob *bytes.Buffer, text []byte, opaque interface{})

@@ -154,30 +154,28 @@ func (elt link_ref_array) Swap(i, j int) {

elt[i], elt[j] = elt[j], elt[i] } -// returns whether or not a line is a reference -func is_ref(data []byte, beg int, last *int, rndr *render) bool { +// is_ref checks whether or not data starts with a reference line. +// For example: +// +// [1]: http://www.google.com/ +// [2]: http://www.github.com/ +// +func is_ref(rndr *render, data []byte) int { // up to 3 optional leading spaces - if beg+3 > len(data) { - return false + if len(data) < 4 { + return 0 } i := 0 - if data[beg] == ' ' { + for i < 3 && data[i] == ' ' { i++ - if data[beg+1] == ' ' { - i++ - if data[beg+2] == ' ' { - i++ - if data[beg+3] == ' ' { - return false - } - } - } } - i += beg + if data[i] == ' ' { + return 0 + } // id part: anything but a newline between brackets if data[i] != '[' { - return false + return 0 } i++ id_offset := i

@@ -185,14 +183,14 @@ for i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != ']' {

i++ } if i >= len(data) || data[i] != ']' { - return false + return 0 } id_end := i // spacer: colon (space | tab)* newline? (space | tab)* i++ if i >= len(data) || data[i] != ':' { - return false + return 0 } i++ for i < len(data) && (data[i] == ' ' || data[i] == '\t') {

@@ -200,7 +198,7 @@ i++

} if i < len(data) && (data[i] == '\n' || data[i] == '\r') { i++ - if i < len(data) && data[i] == '\r' && data[i-1] == '\n' { + if i < len(data) && data[i] == '\n' && data[i-1] == '\r' { i++ } }

@@ -208,7 +206,7 @@ for i < len(data) && (data[i] == ' ' || data[i] == '\t') {

i++ } if i >= len(data) { - return false + return 0 } // link: whitespace-free sequence, optionally between angle brackets

@@ -219,11 +217,10 @@ link_offset := i

for i < len(data) && data[i] != ' ' && data[i] != '\t' && data[i] != '\n' && data[i] != '\r' { i++ } - var link_end int - if data[i-1] == '>' { - link_end = i - 1 - } else { - link_end = i + link_end := i + if data[link_offset] == '<' && data[link_end-1] == '>' { + link_offset++ + link_end-- } // optional spacer: (space | tab)* (newline | '\'' | '"' | '(' )

@@ -231,7 +228,7 @@ for i < len(data) && (data[i] == ' ' || data[i] == '\t') {

i++ } if i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != '\'' && data[i] != '"' && data[i] != '(' { - return false + return 0 } // compute end-of-line

@@ -239,8 +236,8 @@ line_end := 0

if i >= len(data) || data[i] == '\r' || data[i] == '\n' { line_end = i } - if i+1 < len(data) && data[i] == '\n' && data[i+1] == '\r' { - line_end = i + 1 + if i+1 < len(data) && data[i] == '\r' && data[i+1] == '\n' { + line_end++ } // optional (space|tab)* spacer after a newline

@@ -278,24 +275,21 @@ title_end = i

} } if line_end == 0 { // garbage after the link - return false + return 0 } - // a valid ref has been found; fill in return structures - if last != nil { - *last = line_end - } + // a valid ref has been found if rndr == nil { - return true + return line_end } item := &link_ref{id: data[id_offset:id_end], link: data[link_offset:link_end], title: data[title_offset:title_end]} rndr.refs = append(rndr.refs, item) - return true + return line_end } type render struct { - mk *mkd_renderer + mk *Renderer refs link_ref_array active_char [256]int ext_flags uint32

@@ -2326,13 +2320,13 @@ HTML_GITHUB_BLOCKCODE

HTML_USE_XHTML ) -type html_renderopts struct { - toc_data struct { +type HtmlOptions struct { + Flags int + close_tag string // how to end singleton tags: usually " />\n", possibly ">\n" + toc_data struct { header_count int current_level int } - flags uint32 - close_tag string } func attr_escape(ob *bytes.Buffer, src []byte) {

@@ -2385,13 +2379,13 @@ }

} func rndr_header(ob *bytes.Buffer, text []byte, level int, opaque interface{}) { - options := opaque.(*html_renderopts) + options := opaque.(*HtmlOptions) if ob.Len() > 0 { ob.WriteByte('\n') } - if options.flags&HTML_TOC != 0 { + if options.Flags&HTML_TOC != 0 { ob.WriteString(fmt.Sprintf("<h%d id=\"toc_%d\">", level, options.toc_data.header_count)) options.toc_data.header_count++ } else {

@@ -2422,7 +2416,7 @@ ob.WriteByte('\n')

} func rndr_hrule(ob *bytes.Buffer, opaque interface{}) { - options := opaque.(*html_renderopts) + options := opaque.(*HtmlOptions) if ob.Len() > 0 { ob.WriteByte('\n')

@@ -2546,7 +2540,7 @@ ob.WriteString("</li>\n")

} func rndr_paragraph(ob *bytes.Buffer, text []byte, opaque interface{}) { - options := opaque.(*html_renderopts) + options := opaque.(*HtmlOptions) i := 0 if ob.Len() > 0 {

@@ -2566,7 +2560,7 @@ return

} ob.WriteString("<p>") - if options.flags&HTML_HARD_WRAP != 0 { + if options.Flags&HTML_HARD_WRAP != 0 { for i < len(text) { org := i for i < len(text) && text[i] != '\n' {

@@ -2592,12 +2586,12 @@ ob.WriteString("</p>\n")

} func rndr_autolink(ob *bytes.Buffer, link []byte, kind int, opaque interface{}) int { - options := opaque.(*html_renderopts) + options := opaque.(*HtmlOptions) if len(link) == 0 { return 0 } - if options.flags&HTML_SAFELINK != 0 && !is_safe_link(link) && kind != MKDA_EMAIL { + if options.Flags&HTML_SAFELINK != 0 && !is_safe_link(link) && kind != MKDA_EMAIL { return 0 }

@@ -2652,7 +2646,7 @@ return 1

} func rndr_image(ob *bytes.Buffer, link []byte, title []byte, alt []byte, opaque interface{}) int { - options := opaque.(*html_renderopts) + options := opaque.(*HtmlOptions) if len(link) == 0 { return 0 }

@@ -2673,16 +2667,16 @@ return 1

} func rndr_linebreak(ob *bytes.Buffer, opaque interface{}) int { - options := opaque.(*html_renderopts) + options := opaque.(*HtmlOptions) ob.WriteString("<br") ob.WriteString(options.close_tag) return 1 } func rndr_link(ob *bytes.Buffer, link []byte, title []byte, content []byte, opaque interface{}) int { - options := opaque.(*html_renderopts) + options := opaque.(*HtmlOptions) - if options.flags&HTML_SAFELINK != 0 && !is_safe_link(link) { + if options.Flags&HTML_SAFELINK != 0 && !is_safe_link(link) { return 0 }

@@ -2703,17 +2697,17 @@ return 1

} func rndr_raw_html_tag(ob *bytes.Buffer, text []byte, opaque interface{}) int { - options := opaque.(*html_renderopts) - if options.flags&HTML_SKIP_HTML != 0 { + options := opaque.(*HtmlOptions) + if options.Flags&HTML_SKIP_HTML != 0 { return 1 } - if options.flags&HTML_SKIP_STYLE != 0 && is_html_tag(text, "style") { + if options.Flags&HTML_SKIP_STYLE != 0 && is_html_tag(text, "style") { return 1 } - if options.flags&HTML_SKIP_LINKS != 0 && is_html_tag(text, "a") { + if options.Flags&HTML_SKIP_LINKS != 0 && is_html_tag(text, "a") { return 1 } - if options.flags&HTML_SKIP_IMAGES != 0 && is_html_tag(text, "img") { + if options.Flags&HTML_SKIP_IMAGES != 0 && is_html_tag(text, "img") { return 1 } ob.Write(text)

@@ -2817,7 +2811,7 @@ i++

} } -func Markdown(ob *bytes.Buffer, ib []byte, rndrer *mkd_renderer, extensions uint32) { +func Markdown(ob *bytes.Buffer, ib []byte, rndrer *Renderer, extensions uint32) { // no point in parsing if we can't render if rndrer == nil { return

@@ -2875,8 +2869,8 @@ // first pass: look for references, copy everything else

text := bytes.NewBuffer(nil) beg, end := 0, 0 for beg < len(ib) { // iterate over lines - if is_ref(ib, beg, &end, rndr) { - beg = end + if end = is_ref(rndr, ib[beg:]); end > 0 { + beg += end } else { // skip to the next line end = beg for end < len(ib) && ib[end] != '\n' && ib[end] != '\r' {

@@ -2928,50 +2922,54 @@ panic("Nesting level did not end at zero")

} } -func Config_html() *mkd_renderer { +func HtmlRenderer(flags int) *Renderer { // configure the rendering engine - rndrer := new(mkd_renderer) - rndrer.blockcode = rndr_blockcode - rndrer.blockquote = rndr_blockquote - rndrer.blockhtml = rndr_raw_block - rndrer.header = rndr_header - rndrer.hrule = rndr_hrule - rndrer.list = rndr_list - rndrer.listitem = rndr_listitem - rndrer.paragraph = rndr_paragraph - rndrer.table = rndr_table - rndrer.table_row = rndr_tablerow - rndrer.table_cell = rndr_tablecell + r := new(Renderer) + r.blockcode = rndr_blockcode + r.blockquote = rndr_blockquote + r.blockhtml = rndr_raw_block + r.header = rndr_header + r.hrule = rndr_hrule + r.list = rndr_list + r.listitem = rndr_listitem + r.paragraph = rndr_paragraph + r.table = rndr_table + r.table_row = rndr_tablerow + r.table_cell = rndr_tablecell - rndrer.autolink = rndr_autolink - rndrer.codespan = rndr_codespan - rndrer.double_emphasis = rndr_double_emphasis - rndrer.emphasis = rndr_emphasis - rndrer.image = rndr_image - rndrer.linebreak = rndr_linebreak - rndrer.link = rndr_link - rndrer.raw_html_tag = rndr_raw_html_tag - rndrer.triple_emphasis = rndr_triple_emphasis - rndrer.strikethrough = rndr_strikethrough + r.autolink = rndr_autolink + r.codespan = rndr_codespan + r.double_emphasis = rndr_double_emphasis + r.emphasis = rndr_emphasis + r.image = rndr_image + r.linebreak = rndr_linebreak + r.link = rndr_link + r.raw_html_tag = rndr_raw_html_tag + r.triple_emphasis = rndr_triple_emphasis + r.strikethrough = rndr_strikethrough - rndrer.normal_text = rndr_normal_text + r.normal_text = rndr_normal_text - rndrer.opaque = &html_renderopts{close_tag: " />\n"} - return rndrer + close_tag := ">\n" + if flags&HTML_USE_XHTML != 0 { + close_tag = " />\n" + } + r.opaque = &HtmlOptions{Flags: flags, close_tag: close_tag} + return r } func main() { // read the input - var ib []byte + var input []byte var err os.Error switch len(os.Args) { case 1: - if ib, err = ioutil.ReadAll(os.Stdin); err != nil { + if input, err = ioutil.ReadAll(os.Stdin); err != nil { fmt.Fprintln(os.Stderr, "Error reading from Stdin:", err) os.Exit(-1) } case 2, 3: - if ib, err = ioutil.ReadFile(os.Args[1]); err != nil { + if input, err = ioutil.ReadFile(os.Args[1]); err != nil { fmt.Fprintln(os.Stderr, "Error reading from", os.Args[1], ":", err) os.Exit(-1) }

@@ -2981,27 +2979,25 @@ os.Exit(-1)

} // call the main renderer function - ob := bytes.NewBuffer(nil) + output := bytes.NewBuffer(nil) var extensions uint32 extensions |= MKDEXT_NO_INTRA_EMPHASIS extensions |= MKDEXT_TABLES extensions |= MKDEXT_FENCED_CODE extensions |= MKDEXT_AUTOLINK extensions |= MKDEXT_STRIKETHROUGH - extensions |= MKDEXT_LAX_HTML_BLOCKS extensions |= MKDEXT_SPACE_HEADERS - extensions = 0 - Markdown(ob, ib, Config_html(), extensions) + Markdown(output, input, HtmlRenderer(HTML_USE_XHTML), extensions) // output the result if len(os.Args) == 3 { - if err = ioutil.WriteFile(os.Args[2], ob.Bytes(), 0644); err != nil { + if err = ioutil.WriteFile(os.Args[2], output.Bytes(), 0644); err != nil { fmt.Fprintln(os.Stderr, "Error writing to", os.Args[2], ":", err) os.Exit(-1) } } else { - if _, err = os.Stdout.Write(ob.Bytes()); err != nil { + if _, err = os.Stdout.Write(output.Bytes()); err != nil { fmt.Fprintln(os.Stderr, "Error writing to Stdout:", err) os.Exit(-1) }