mirror of
https://github.com/russross/blackfriday.git
synced 2024-03-22 13:40:34 +08:00
Merge pull request #376 from russross/v2-improve-renderer-368
v2: improve Renderer and fix #368
This commit is contained in:
commit
f86f06b532
|
@ -134,7 +134,7 @@ All features of Sundown are supported, including:
|
|||
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
|
||||
|
|
|
@ -1606,36 +1606,6 @@ func TestTOC(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
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*",
|
||||
|
|
103
html.go
103
html.go
|
@ -45,7 +45,6 @@ const (
|
|||
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 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt
|
|||
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 @@ func (r *HTMLRenderer) writeTOC(w *bytes.Buffer, ast *Node) {
|
|||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
|
46
markdown.go
46
markdown.go
|
@ -134,22 +134,33 @@ var blockTags = map[string]struct{}{
|
|||
"video": struct{}{},
|
||||
}
|
||||
|
||||
// Renderer is the rendering interface.
|
||||
// This is mostly of interest if you are implementing a new rendering format.
|
||||
// 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.
|
||||
//
|
||||
// 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 @@ func Run(input []byte, opts ...Option) []byte {
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue
Block a user