all repos — grayfriday @ f86f06b532ba7bdb7e24dc77c9bbb8c737b018dc

blackfriday fork with a few changes

Merge pull request #376 from russross/v2-improve-renderer-368

v2: improve Renderer and fix #368
Vytautas Ĺ altenis vytas@rtfb.lt
Wed, 26 Jul 2017 21:42:39 +0300
commit

f86f06b532ba7bdb7e24dc77c9bbb8c737b018dc

parent

70c446a327c4e69c0f661dc092d9729c3af7472c

4 files changed, 79 insertions(+), 102 deletions(-)

jump to
M README.mdREADME.md

@@ -134,7 +134,7 @@ known inputs that make it crash. If you find one, please let me

know and send me the input that does it. NOTE: "safety" in this context means *runtime safety only*. In order to - protect yourself agains JavaScript injection in untrusted content, see + protect yourself against JavaScript injection in untrusted content, see [this example](https://github.com/russross/blackfriday#sanitize-untrusted-content). * **Fast processing**. It is fast enough to render on-demand in
M block_test.goblock_test.go

@@ -1606,36 +1606,6 @@ HTMLFlags: UseXHTML | TOC,

}) } -func TestOmitContents(t *testing.T) { - var tests = []string{ - "# Title\n\n##Subtitle\n\n#Title2", - `<nav> - -<ul> -<li><a href="#toc_0">Title</a> -<ul> -<li><a href="#toc_1">Subtitle</a></li> -</ul></li> - -<li><a href="#toc_2">Title2</a></li> -</ul> - -</nav> -`, - - // Make sure OmitContents omits even with no TOC - "#\n\nfoo", - "", - } - doTestsParam(t, tests, TestParams{ - HTMLFlags: UseXHTML | TOC | OmitContents, - }) - // Now run again: make sure OmitContents implies TOC - doTestsParam(t, tests, TestParams{ - HTMLFlags: UseXHTML | OmitContents, - }) -} - func TestCompletePage(t *testing.T) { var tests = []string{ "*foo*",
M html.gohtml.go

@@ -45,7 +45,6 @@ SmartypantsDashes // Enable smart dashes (with Smartypants)

SmartypantsLatexDashes // Enable LaTeX-style dashes (with Smartypants) SmartypantsAngledQuotes // Enable angled double quotes (with Smartypants) for double quotes rendering TOC // Generate a table of contents - OmitContents // Skip the main contents (for a standalone table of contents) TagName = "[A-Za-z][A-Za-z0-9-]*" AttributeName = "[a-zA-Z_:][a-zA-Z0-9:._-]*"

@@ -819,55 +818,71 @@ }

return GoToNext } -func (r *HTMLRenderer) writeDocumentHeader(w *bytes.Buffer) { +// RenderHeader writes HTML document preamble and TOC if requested. +func (r *HTMLRenderer) RenderHeader(w io.Writer, ast *Node) { + r.writeDocumentHeader(w) + if r.Flags&TOC != 0 { + r.writeTOC(w, ast) + } +} + +// RenderFooter writes HTML document footer. +func (r *HTMLRenderer) RenderFooter(w io.Writer, ast *Node) { + if r.Flags&CompletePage == 0 { + return + } + io.WriteString(w, "\n</body>\n</html>\n") +} + +func (r *HTMLRenderer) writeDocumentHeader(w io.Writer) { if r.Flags&CompletePage == 0 { return } ending := "" if r.Flags&UseXHTML != 0 { - w.WriteString("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ") - w.WriteString("\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n") - w.WriteString("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n") + io.WriteString(w, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ") + io.WriteString(w, "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n") + io.WriteString(w, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n") ending = " /" } else { - w.WriteString("<!DOCTYPE html>\n") - w.WriteString("<html>\n") + io.WriteString(w, "<!DOCTYPE html>\n") + io.WriteString(w, "<html>\n") } - w.WriteString("<head>\n") - w.WriteString(" <title>") + io.WriteString(w, "<head>\n") + io.WriteString(w, " <title>") if r.Flags&Smartypants != 0 { r.sr.Process(w, []byte(r.Title)) } else { escapeHTML(w, []byte(r.Title)) } - w.WriteString("</title>\n") - w.WriteString(" <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v") - w.WriteString(Version) - w.WriteString("\"") - w.WriteString(ending) - w.WriteString(">\n") - w.WriteString(" <meta charset=\"utf-8\"") - w.WriteString(ending) - w.WriteString(">\n") + io.WriteString(w, "</title>\n") + io.WriteString(w, " <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v") + io.WriteString(w, Version) + io.WriteString(w, "\"") + io.WriteString(w, ending) + io.WriteString(w, ">\n") + io.WriteString(w, " <meta charset=\"utf-8\"") + io.WriteString(w, ending) + io.WriteString(w, ">\n") if r.CSS != "" { - w.WriteString(" <link rel=\"stylesheet\" type=\"text/css\" href=\"") + io.WriteString(w, " <link rel=\"stylesheet\" type=\"text/css\" href=\"") escapeHTML(w, []byte(r.CSS)) - w.WriteString("\"") - w.WriteString(ending) - w.WriteString(">\n") + io.WriteString(w, "\"") + io.WriteString(w, ending) + io.WriteString(w, ">\n") } if r.Icon != "" { - w.WriteString(" <link rel=\"icon\" type=\"image/x-icon\" href=\"") + io.WriteString(w, " <link rel=\"icon\" type=\"image/x-icon\" href=\"") escapeHTML(w, []byte(r.Icon)) - w.WriteString("\"") - w.WriteString(ending) - w.WriteString(">\n") + io.WriteString(w, "\"") + io.WriteString(w, ending) + io.WriteString(w, ">\n") } - w.WriteString("</head>\n") - w.WriteString("<body>\n\n") + io.WriteString(w, "</head>\n") + io.WriteString(w, "<body>\n\n") } -func (r *HTMLRenderer) writeTOC(w *bytes.Buffer, ast *Node) { +func (r *HTMLRenderer) writeTOC(w io.Writer, ast *Node) { buf := bytes.Buffer{} inHeading := false

@@ -914,35 +929,9 @@ buf.WriteString("</li>\n</ul>")

} if buf.Len() > 0 { - w.WriteString("<nav>\n") + io.WriteString(w, "<nav>\n") w.Write(buf.Bytes()) - w.WriteString("\n\n</nav>\n") + io.WriteString(w, "\n\n</nav>\n") } r.lastOutputLen = buf.Len() } - -func (r *HTMLRenderer) writeDocumentFooter(w *bytes.Buffer) { - if r.Flags&CompletePage == 0 { - return - } - w.WriteString("\n</body>\n</html>\n") -} - -// Render walks the specified syntax (sub)tree and returns a HTML document. -func (r *HTMLRenderer) Render(ast *Node) []byte { - //println("render_Blackfriday") - //dump(ast) - var buf bytes.Buffer - r.writeDocumentHeader(&buf) - if r.Flags&TOC != 0 || r.Flags&OmitContents != 0 { - r.writeTOC(&buf, ast) - if r.Flags&OmitContents != 0 { - return buf.Bytes() - } - } - ast.Walk(func(node *Node, entering bool) WalkStatus { - return r.RenderNode(&buf, node, entering) - }) - r.writeDocumentFooter(&buf) - return buf.Bytes() -}
M markdown.gomarkdown.go

@@ -134,22 +134,33 @@ "section": struct{}{},

"video": struct{}{}, } -// Renderer is the rendering interface. -// This is mostly of interest if you are implementing a new rendering format. -// -// When a byte slice is provided, it contains the (rendered) contents of the -// element. +// Renderer is the rendering interface. This is mostly of interest if you are +// implementing a new rendering format. // -// When a callback is provided instead, it will write the contents of the -// respective element directly to the output buffer and return true on success. -// If the callback returns false, the rendering function should reset the -// output buffer as though it had never been called. -// -// Only an HTML implementation is provided in this repository, -// see the README for external implementations. +// Only an HTML implementation is provided in this repository, see the README +// for external implementations. type Renderer interface { - Render(ast *Node) []byte + // RenderNode is the main rendering method. It will be called once for + // every leaf node and twice for every non-leaf node (first with + // entering=true, then with entering=false). The method should write its + // rendition of the node to the supplied writer w. RenderNode(w io.Writer, node *Node, entering bool) WalkStatus + + // RenderHeader is a method that allows the renderer to produce some + // content preceding the main body of the output document. The header is + // understood in the broad sense here. For example, the default HTML + // renderer will write not only the HTML document preamble, but also the + // table of contents if it was requested. + // + // The method will be passed an entire document tree, in case a particular + // implementation needs to inspect it to produce output. + // + // The output should be written to the supplied writer w. If your + // implementation has no header to write, supply an empty implementation. + RenderHeader(w io.Writer, ast *Node) + + // RenderFooter is a symmetric counterpart of RenderHeader. + RenderFooter(w io.Writer, ast *Node) } // Callback functions for inline parsing. One such function is defined

@@ -374,7 +385,14 @@ })

optList := []Option{WithRenderer(r), WithExtensions(CommonExtensions)} optList = append(optList, opts...) parser := New(optList...) - return parser.renderer.Render(parser.Parse(input)) + ast := parser.Parse(input) + var buf bytes.Buffer + parser.renderer.RenderHeader(&buf, ast) + ast.Walk(func(node *Node, entering bool) WalkStatus { + return parser.renderer.RenderNode(&buf, node, entering) + }) + parser.renderer.RenderFooter(&buf, ast) + return buf.Bytes() } // Parse is an entry point to the parsing part of Blackfriday. It takes an