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 }