Clean up Renderer interface: remove all callbacks We now expose the structure of the document in the form of AST, there's no longer need to have all those callbacks to represent structure.
Vytautas Šaltenis vytas@rtfb.lt
Mon, 11 Apr 2016 11:22:38 +0300
2 files changed,
0 insertions(+),
634 deletions(-)
M
html.go
→
html.go
@@ -21,7 +21,6 @@ "fmt"
"html" "io" "regexp" - "strconv" "strings" )@@ -200,597 +199,6 @@ r.w.Write(src[rang[0]:rang[1]])
end = rang[1] } r.attrEscape(src[end:]) -} - -func (r *HTML) TitleBlock(text []byte) { - text = bytes.TrimPrefix(text, []byte("% ")) - text = bytes.Replace(text, []byte("\n% "), []byte("\n"), -1) - r.w.WriteString("<h1 class=\"title\">") - r.w.Write(text) - r.w.WriteString("\n</h1>") -} - -func (r *HTML) BeginHeader(level int, id string) { - r.w.Newline() - - if id == "" && r.Extensions&TOC != 0 { - id = fmt.Sprintf("toc_%d", r.headerCount) - } - - if id != "" { - id = r.ensureUniqueHeaderID(id) - - if r.HeaderIDPrefix != "" { - id = r.HeaderIDPrefix + id - } - - if r.HeaderIDSuffix != "" { - id = id + r.HeaderIDSuffix - } - - r.w.WriteString(fmt.Sprintf("<h%d id=\"%s\">", level, id)) - } else { - r.w.WriteString(fmt.Sprintf("<h%d>", level)) - } -} - -func (r *HTML) EndHeader(level int, id string, header []byte) { - // are we building a table of contents? - if r.Extensions&TOC != 0 { - r.TocHeaderWithAnchor(header, level, id) - } - - r.w.WriteString(fmt.Sprintf("</h%d>\n", level)) -} - -func (r *HTML) BlockHtml(text []byte) { - if r.Flags&SkipHTML != 0 { - return - } - - r.w.Newline() - r.w.Write(text) - r.w.WriteByte('\n') -} - -func (r *HTML) HRule() { - r.w.Newline() - r.w.WriteString("<hr") - r.w.WriteString(r.closeTag) - r.w.WriteByte('\n') -} - -func (r *HTML) BlockCode(text []byte, lang string) { - r.w.Newline() - - // parse out the language names/classes - count := 0 - for _, elt := range strings.Fields(lang) { - if elt[0] == '.' { - elt = elt[1:] - } - if len(elt) == 0 { - continue - } - if count == 0 { - r.w.WriteString("<pre><code class=\"language-") - } else { - r.w.WriteByte(' ') - } - r.attrEscape([]byte(elt)) - count++ - } - - if count == 0 { - r.w.WriteString("<pre><code>") - } else { - r.w.WriteString("\">") - } - - r.attrEscape(text) - r.w.WriteString("</code></pre>\n") -} - -func (r *HTML) BlockQuote(text []byte) { - r.w.Newline() - r.w.WriteString("<blockquote>\n") - r.w.Write(text) - r.w.WriteString("</blockquote>\n") -} - -func (r *HTML) Table(header []byte, body []byte, columnData []CellAlignFlags) { - r.w.Newline() - r.w.WriteString("<table>\n<thead>\n") - r.w.Write(header) - r.w.WriteString("</thead>\n\n<tbody>\n") - r.w.Write(body) - r.w.WriteString("</tbody>\n</table>\n") -} - -func (r *HTML) TableRow(text []byte) { - r.w.Newline() - r.w.WriteString("<tr>\n") - r.w.Write(text) - r.w.WriteString("\n</tr>\n") -} - -func leadingNewline(out *bytes.Buffer) { - if out.Len() > 0 { - out.WriteByte('\n') - } -} - -func (r *HTML) TableHeaderCell(out *bytes.Buffer, text []byte, align CellAlignFlags) { - leadingNewline(out) - switch align { - case TableAlignmentLeft: - out.WriteString("<th align=\"left\">") - case TableAlignmentRight: - out.WriteString("<th align=\"right\">") - case TableAlignmentCenter: - out.WriteString("<th align=\"center\">") - default: - out.WriteString("<th>") - } - - out.Write(text) - out.WriteString("</th>") -} - -func (r *HTML) TableCell(out *bytes.Buffer, text []byte, align CellAlignFlags) { - leadingNewline(out) - switch align { - case TableAlignmentLeft: - out.WriteString("<td align=\"left\">") - case TableAlignmentRight: - out.WriteString("<td align=\"right\">") - case TableAlignmentCenter: - out.WriteString("<td align=\"center\">") - default: - out.WriteString("<td>") - } - - out.Write(text) - out.WriteString("</td>") -} - -func (r *HTML) BeginFootnotes() { - r.w.WriteString("<div class=\"footnotes\">\n") - r.HRule() - r.BeginList(ListTypeOrdered) -} - -func (r *HTML) EndFootnotes() { - r.EndList(ListTypeOrdered) - r.w.WriteString("</div>\n") -} - -func (r *HTML) FootnoteItem(name, text []byte, flags ListType) { - if flags&ListItemContainsBlock != 0 || flags&ListItemBeginningOfList != 0 { - r.w.Newline() - } - slug := slugify(name) - r.w.WriteString(`<li id="`) - r.w.WriteString(`fn:`) - r.w.WriteString(r.FootnoteAnchorPrefix) - r.w.Write(slug) - r.w.WriteString(`">`) - r.w.Write(text) - if r.Flags&FootnoteReturnLinks != 0 { - r.w.WriteString(` <a class="footnote-return" href="#`) - r.w.WriteString(`fnref:`) - r.w.WriteString(r.FootnoteAnchorPrefix) - r.w.Write(slug) - r.w.WriteString(`">`) - r.w.WriteString(r.FootnoteReturnLinkContents) - r.w.WriteString(`</a>`) - } - r.w.WriteString("</li>\n") -} - -func (r *HTML) BeginList(flags ListType) { - r.w.Newline() - - if flags&ListTypeDefinition != 0 { - r.w.WriteString("<dl>") - } else if flags&ListTypeOrdered != 0 { - r.w.WriteString("<ol>") - } else { - r.w.WriteString("<ul>") - } -} - -func (r *HTML) EndList(flags ListType) { - if flags&ListTypeDefinition != 0 { - r.w.WriteString("</dl>\n") - } else if flags&ListTypeOrdered != 0 { - r.w.WriteString("</ol>\n") - } else { - r.w.WriteString("</ul>\n") - } -} - -func (r *HTML) ListItem(text []byte, flags ListType) { - if (flags&ListItemContainsBlock != 0 && flags&ListTypeDefinition == 0) || - flags&ListItemBeginningOfList != 0 { - r.w.Newline() - } - if flags&ListTypeTerm != 0 { - r.w.WriteString("<dt>") - } else if flags&ListTypeDefinition != 0 { - r.w.WriteString("<dd>") - } else { - r.w.WriteString("<li>") - } - r.w.Write(text) - if flags&ListTypeTerm != 0 { - r.w.WriteString("</dt>\n") - } else if flags&ListTypeDefinition != 0 { - r.w.WriteString("</dd>\n") - } else { - r.w.WriteString("</li>\n") - } -} - -func (r *HTML) BeginParagraph() { - r.w.Newline() - r.w.WriteString("<p>") -} - -func (r *HTML) EndParagraph() { - r.w.WriteString("</p>\n") -} - -func (r *HTML) AutoLink(link []byte, kind LinkType) { - skipRanges := htmlEntity.FindAllIndex(link, -1) - if r.Flags&Safelink != 0 && !isSafeLink(link) && kind != LinkTypeEmail { - // mark it but don't link it if it is not a safe link: no smartypants - r.w.WriteString("<tt>") - r.entityEscapeWithSkip(link, skipRanges) - r.w.WriteString("</tt>") - return - } - - r.w.WriteString("<a href=\"") - if kind == LinkTypeEmail { - r.w.WriteString("mailto:") - } else { - r.maybeWriteAbsolutePrefix(link) - } - - r.entityEscapeWithSkip(link, skipRanges) - - var relAttrs []string - if r.Flags&NofollowLinks != 0 && !isRelativeLink(link) { - relAttrs = append(relAttrs, "nofollow") - } - if r.Flags&NoreferrerLinks != 0 && !isRelativeLink(link) { - relAttrs = append(relAttrs, "noreferrer") - } - if len(relAttrs) > 0 { - r.w.WriteString(fmt.Sprintf("\" rel=\"%s", strings.Join(relAttrs, " "))) - } - - // blank target only add to external link - if r.Flags&HrefTargetBlank != 0 && !isRelativeLink(link) { - r.w.WriteString("\" target=\"_blank") - } - - r.w.WriteString("\">") - - // Pretty print: if we get an email address as - // an actual URI, e.g. `mailto:foo@bar.com`, we don't - // want to print the `mailto:` prefix - switch { - case bytes.HasPrefix(link, []byte("mailto://")): - r.attrEscape(link[len("mailto://"):]) - case bytes.HasPrefix(link, []byte("mailto:")): - r.attrEscape(link[len("mailto:"):]) - default: - r.entityEscapeWithSkip(link, skipRanges) - } - - r.w.WriteString("</a>") -} - -func (r *HTML) CodeSpan(text []byte) { - r.w.WriteString("<code>") - r.attrEscape(text) - r.w.WriteString("</code>") -} - -func (r *HTML) DoubleEmphasis(text []byte) { - r.w.WriteString("<strong>") - r.w.Write(text) - r.w.WriteString("</strong>") -} - -func (r *HTML) Emphasis(text []byte) { - if len(text) == 0 { - return - } - r.w.WriteString("<em>") - r.w.Write(text) - r.w.WriteString("</em>") -} - -func (r *HTML) maybeWriteAbsolutePrefix(link []byte) { - if r.AbsolutePrefix != "" && isRelativeLink(link) && link[0] != '.' { - r.w.WriteString(r.AbsolutePrefix) - if link[0] != '/' { - r.w.WriteByte('/') - } - } -} - -func (r *HTML) Image(link []byte, title []byte, alt []byte) { - if r.Flags&SkipImages != 0 { - return - } - - r.w.WriteString("<img src=\"") - r.maybeWriteAbsolutePrefix(link) - r.attrEscape(link) - r.w.WriteString("\" alt=\"") - if len(alt) > 0 { - r.attrEscape(alt) - } - if len(title) > 0 { - r.w.WriteString("\" title=\"") - r.attrEscape(title) - } - - r.w.WriteByte('"') - r.w.WriteString(r.closeTag) -} - -func (r *HTML) LineBreak() { - r.w.WriteString("<br") - r.w.WriteString(r.closeTag) - r.w.WriteByte('\n') -} - -func (r *HTML) Link(link []byte, title []byte, content []byte) { - if r.Flags&SkipLinks != 0 { - // write the link text out but don't link it, just mark it with typewriter font - r.w.WriteString("<tt>") - r.attrEscape(content) - r.w.WriteString("</tt>") - return - } - - if r.Flags&Safelink != 0 && !isSafeLink(link) { - // write the link text out but don't link it, just mark it with typewriter font - r.w.WriteString("<tt>") - r.attrEscape(content) - r.w.WriteString("</tt>") - return - } - - r.w.WriteString("<a href=\"") - r.maybeWriteAbsolutePrefix(link) - r.attrEscape(link) - if len(title) > 0 { - r.w.WriteString("\" title=\"") - r.attrEscape(title) - } - var relAttrs []string - if r.Flags&NofollowLinks != 0 && !isRelativeLink(link) { - relAttrs = append(relAttrs, "nofollow") - } - if r.Flags&NoreferrerLinks != 0 && !isRelativeLink(link) { - relAttrs = append(relAttrs, "noreferrer") - } - if len(relAttrs) > 0 { - r.w.WriteString(fmt.Sprintf("\" rel=\"%s", strings.Join(relAttrs, " "))) - } - - // blank target only add to external link - if r.Flags&HrefTargetBlank != 0 && !isRelativeLink(link) { - r.w.WriteString("\" target=\"_blank") - } - - r.w.WriteString("\">") - r.w.Write(content) - r.w.WriteString("</a>") - return -} - -func (r *HTML) RawHtmlTag(text []byte) { - if r.Flags&SkipHTML != 0 { - return - } - if r.Flags&SkipStyle != 0 && isHtmlTag(text, "style") { - return - } - if r.Flags&SkipLinks != 0 && isHtmlTag(text, "a") { - return - } - if r.Flags&SkipImages != 0 && isHtmlTag(text, "img") { - return - } - r.w.Write(text) -} - -func (r *HTML) TripleEmphasis(text []byte) { - r.w.WriteString("<strong><em>") - r.w.Write(text) - r.w.WriteString("</em></strong>") -} - -func (r *HTML) StrikeThrough(text []byte) { - r.w.WriteString("<del>") - r.w.Write(text) - r.w.WriteString("</del>") -} - -func (r *HTML) FootnoteRef(ref []byte, id int) { - slug := slugify(ref) - r.w.WriteString(`<sup class="footnote-ref" id="`) - r.w.WriteString(`fnref:`) - r.w.WriteString(r.FootnoteAnchorPrefix) - r.w.Write(slug) - r.w.WriteString(`"><a rel="footnote" href="#`) - r.w.WriteString(`fn:`) - r.w.WriteString(r.FootnoteAnchorPrefix) - r.w.Write(slug) - r.w.WriteString(`">`) - r.w.WriteString(strconv.Itoa(id)) - r.w.WriteString(`</a></sup>`) -} - -func (r *HTML) Entity(entity []byte) { - r.w.Write(entity) -} - -func (r *HTML) NormalText(text []byte) { - if r.Extensions&Smartypants != 0 { - r.Smartypants(text) - } else { - r.attrEscape(text) - } -} - -func (r *HTML) Smartypants(text []byte) { - r.w.Write(NewSmartypantsRenderer(r.Extensions).Process(text)) -} - -func (r *HTML) DocumentHeader() { - if r.Flags&CompletePage == 0 { - return - } - - ending := "" - if r.Flags&UseXHTML != 0 { - r.w.WriteString("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ") - r.w.WriteString("\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n") - r.w.WriteString("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n") - ending = " /" - } else { - r.w.WriteString("<!DOCTYPE html>\n") - r.w.WriteString("<html>\n") - } - r.w.WriteString("<head>\n") - r.w.WriteString(" <title>") - r.NormalText([]byte(r.Title)) - r.w.WriteString("</title>\n") - r.w.WriteString(" <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v") - r.w.WriteString(VERSION) - r.w.WriteString("\"") - r.w.WriteString(ending) - r.w.WriteString(">\n") - r.w.WriteString(" <meta charset=\"utf-8\"") - r.w.WriteString(ending) - r.w.WriteString(">\n") - if r.CSS != "" { - r.w.WriteString(" <link rel=\"stylesheet\" type=\"text/css\" href=\"") - r.attrEscape([]byte(r.CSS)) - r.w.WriteString("\"") - r.w.WriteString(ending) - r.w.WriteString(">\n") - } - r.w.WriteString("</head>\n") - r.w.WriteString("<body>\n") - - r.tocMarker = r.w.Len() // XXX -} - -func (r *HTML) DocumentFooter() { - // finalize and insert the table of contents - if r.Extensions&TOC != 0 { - r.TocFinalize() - - // now we have to insert the table of contents into the document - var temp bytes.Buffer - - // start by making a copy of everything after the document header - temp.Write(r.w.Bytes()[r.tocMarker:]) - - // now clear the copied material from the main output buffer - r.w.Truncate(r.tocMarker) - - // corner case spacing issue - if r.Flags&CompletePage != 0 { - r.w.WriteByte('\n') - } - - // insert the table of contents - r.w.WriteString("<nav>\n") - r.w.Write(r.toc.Bytes()) - r.w.WriteString("</nav>\n") - - // corner case spacing issue - if r.Flags&CompletePage == 0 && r.Extensions&OmitContents == 0 { - r.w.WriteByte('\n') - } - - // write out everything that came after it - if r.Extensions&OmitContents == 0 { - r.w.Write(temp.Bytes()) - } - } - - if r.Flags&CompletePage != 0 { - r.w.WriteString("\n</body>\n") - r.w.WriteString("</html>\n") - } - -} - -func (r *HTML) TocHeaderWithAnchor(text []byte, level int, anchor string) { - for level > r.currentLevel { - switch { - case bytes.HasSuffix(r.toc.Bytes(), []byte("</li>\n")): - // this sublist can nest underneath a header - size := r.toc.Len() - r.toc.Truncate(size - len("</li>\n")) - - case r.currentLevel > 0: - r.toc.WriteString("<li>") - } - if r.toc.Len() > 0 { - r.toc.WriteByte('\n') - } - r.toc.WriteString("<ul>\n") - r.currentLevel++ - } - - for level < r.currentLevel { - r.toc.WriteString("</ul>") - if r.currentLevel > 1 { - r.toc.WriteString("</li>\n") - } - r.currentLevel-- - } - - r.toc.WriteString("<li><a href=\"#") - if anchor != "" { - r.toc.WriteString(anchor) - } else { - r.toc.WriteString("toc_") - r.toc.WriteString(strconv.Itoa(r.headerCount)) - } - r.toc.WriteString("\">") - r.headerCount++ - - r.toc.Write(text) - - r.toc.WriteString("</a></li>\n") -} - -func (r *HTML) TocHeader(text []byte, level int) { - r.TocHeaderWithAnchor(text, level, "") -} - -func (r *HTML) TocFinalize() { - for r.currentLevel > 1 { - r.toc.WriteString("</ul></li>\n") - r.currentLevel-- - } - - if r.currentLevel > 0 { - r.toc.WriteString("</ul>\n") - } } func isHtmlTag(tag []byte, tagname string) bool {
M
markdown.go
→
markdown.go
@@ -170,48 +170,6 @@ // output buffer as though it had never been called.
// // Currently Html and Latex implementations are provided type Renderer interface { - // block-level callbacks - BlockCode(text []byte, lang string) - BlockQuote(text []byte) - BlockHtml(text []byte) - BeginHeader(level int, id string) - EndHeader(level int, id string, header []byte) - HRule() - BeginList(flags ListType) - EndList(flags ListType) - ListItem(text []byte, flags ListType) - BeginParagraph() - EndParagraph() - Table(header []byte, body []byte, columnData []CellAlignFlags) - TableRow(text []byte) - TableHeaderCell(out *bytes.Buffer, text []byte, flags CellAlignFlags) - TableCell(out *bytes.Buffer, text []byte, flags CellAlignFlags) - BeginFootnotes() - EndFootnotes() - FootnoteItem(name, text []byte, flags ListType) - TitleBlock(text []byte) - - // Span-level callbacks - AutoLink(link []byte, kind LinkType) - CodeSpan(text []byte) - DoubleEmphasis(text []byte) - Emphasis(text []byte) - Image(link []byte, title []byte, alt []byte) - LineBreak() - Link(link []byte, title []byte, content []byte) - RawHtmlTag(tag []byte) - TripleEmphasis(text []byte) - StrikeThrough(text []byte) - FootnoteRef(ref []byte, id int) - - // Low-level callbacks - Entity(entity []byte) - NormalText(text []byte) - - // Header and footer - DocumentHeader() - DocumentFooter() - Render(ast *Node) []byte }