From c207eca993c2bdbe6130ac017352ea5614eb2bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vytautas=20=C5=A0altenis?= Date: Mon, 11 Apr 2016 11:22:38 +0300 Subject: [PATCH] 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. --- html.go | 592 ---------------------------------------------------- markdown.go | 42 ---- 2 files changed, 634 deletions(-) diff --git a/html.go b/html.go index d8a681e..e344f91 100644 --- a/html.go +++ b/html.go @@ -21,7 +21,6 @@ import ( "html" "io" "regexp" - "strconv" "strings" ) @@ -202,597 +201,6 @@ func (r *HTML) entityEscapeWithSkip(src []byte, skipRanges [][]int) { 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("

") - r.w.Write(text) - r.w.WriteString("\n

") -} - -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("", level, id)) - } else { - r.w.WriteString(fmt.Sprintf("", 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("\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("") - } else { - r.w.WriteString("\">") - } - - r.attrEscape(text) - r.w.WriteString("\n") -} - -func (r *HTML) BlockQuote(text []byte) { - r.w.Newline() - r.w.WriteString("
\n") - r.w.Write(text) - r.w.WriteString("
\n") -} - -func (r *HTML) Table(header []byte, body []byte, columnData []CellAlignFlags) { - r.w.Newline() - r.w.WriteString("\n\n") - r.w.Write(header) - r.w.WriteString("\n\n\n") - r.w.Write(body) - r.w.WriteString("\n
\n") -} - -func (r *HTML) TableRow(text []byte) { - r.w.Newline() - r.w.WriteString("\n") - r.w.Write(text) - r.w.WriteString("\n\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("") - case TableAlignmentRight: - out.WriteString("") - case TableAlignmentCenter: - out.WriteString("") - default: - out.WriteString("") - } - - out.Write(text) - out.WriteString("") -} - -func (r *HTML) TableCell(out *bytes.Buffer, text []byte, align CellAlignFlags) { - leadingNewline(out) - switch align { - case TableAlignmentLeft: - out.WriteString("") - case TableAlignmentRight: - out.WriteString("") - case TableAlignmentCenter: - out.WriteString("") - default: - out.WriteString("") - } - - out.Write(text) - out.WriteString("") -} - -func (r *HTML) BeginFootnotes() { - r.w.WriteString("
\n") - r.HRule() - r.BeginList(ListTypeOrdered) -} - -func (r *HTML) EndFootnotes() { - r.EndList(ListTypeOrdered) - r.w.WriteString("
\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(`
  • `) - r.w.Write(text) - if r.Flags&FootnoteReturnLinks != 0 { - r.w.WriteString(` `) - r.w.WriteString(r.FootnoteReturnLinkContents) - r.w.WriteString(``) - } - r.w.WriteString("
  • \n") -} - -func (r *HTML) BeginList(flags ListType) { - r.w.Newline() - - if flags&ListTypeDefinition != 0 { - r.w.WriteString("
    ") - } else if flags&ListTypeOrdered != 0 { - r.w.WriteString("
      ") - } else { - r.w.WriteString("
        ") - } -} - -func (r *HTML) EndList(flags ListType) { - if flags&ListTypeDefinition != 0 { - r.w.WriteString("
    \n") - } else if flags&ListTypeOrdered != 0 { - r.w.WriteString("\n") - } else { - r.w.WriteString("\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("
    ") - } else if flags&ListTypeDefinition != 0 { - r.w.WriteString("
    ") - } else { - r.w.WriteString("
  • ") - } - r.w.Write(text) - if flags&ListTypeTerm != 0 { - r.w.WriteString("\n") - } else if flags&ListTypeDefinition != 0 { - r.w.WriteString("
  • \n") - } else { - r.w.WriteString("\n") - } -} - -func (r *HTML) BeginParagraph() { - r.w.Newline() - r.w.WriteString("

    ") -} - -func (r *HTML) EndParagraph() { - r.w.WriteString("

    \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("") - r.entityEscapeWithSkip(link, skipRanges) - r.w.WriteString("") - return - } - - r.w.WriteString(" 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("") -} - -func (r *HTML) CodeSpan(text []byte) { - r.w.WriteString("") - r.attrEscape(text) - r.w.WriteString("") -} - -func (r *HTML) DoubleEmphasis(text []byte) { - r.w.WriteString("") - r.w.Write(text) - r.w.WriteString("") -} - -func (r *HTML) Emphasis(text []byte) { - if len(text) == 0 { - return - } - r.w.WriteString("") - r.w.Write(text) - r.w.WriteString("") -} - -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("\"") 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("") - r.attrEscape(content) - r.w.WriteString("") - 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("") - r.attrEscape(content) - r.w.WriteString("") - return - } - - r.w.WriteString(" 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("") - 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("") - r.w.Write(text) - r.w.WriteString("") -} - -func (r *HTML) StrikeThrough(text []byte) { - r.w.WriteString("") - r.w.Write(text) - r.w.WriteString("") -} - -func (r *HTML) FootnoteRef(ref []byte, id int) { - slug := slugify(ref) - r.w.WriteString(``) - r.w.WriteString(strconv.Itoa(id)) - r.w.WriteString(``) -} - -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("\n") - r.w.WriteString("\n") - ending = " /" - } else { - r.w.WriteString("\n") - r.w.WriteString("\n") - } - r.w.WriteString("\n") - r.w.WriteString(" ") - r.NormalText([]byte(r.Title)) - r.w.WriteString("\n") - r.w.WriteString(" \n") - r.w.WriteString(" \n") - if r.CSS != "" { - r.w.WriteString(" \n") - } - r.w.WriteString("\n") - r.w.WriteString("\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("\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\n") - r.w.WriteString("\n") - } - -} - -func (r *HTML) TocHeaderWithAnchor(text []byte, level int, anchor string) { - for level > r.currentLevel { - switch { - case bytes.HasSuffix(r.toc.Bytes(), []byte("\n")): - // this sublist can nest underneath a header - size := r.toc.Len() - r.toc.Truncate(size - len("\n")) - - case r.currentLevel > 0: - r.toc.WriteString("
  • ") - } - if r.toc.Len() > 0 { - r.toc.WriteByte('\n') - } - r.toc.WriteString("
      \n") - r.currentLevel++ - } - - for level < r.currentLevel { - r.toc.WriteString("
    ") - if r.currentLevel > 1 { - r.toc.WriteString("
  • \n") - } - r.currentLevel-- - } - - r.toc.WriteString("
  • ") - r.headerCount++ - - r.toc.Write(text) - - r.toc.WriteString("
  • \n") -} - -func (r *HTML) TocHeader(text []byte, level int) { - r.TocHeaderWithAnchor(text, level, "") -} - -func (r *HTML) TocFinalize() { - for r.currentLevel > 1 { - r.toc.WriteString("\n") - r.currentLevel-- - } - - if r.currentLevel > 0 { - r.toc.WriteString("\n") - } -} - func isHtmlTag(tag []byte, tagname string) bool { found, _ := findHtmlTagPos(tag, tagname) return found diff --git a/markdown.go b/markdown.go index 03936c5..f3df318 100644 --- a/markdown.go +++ b/markdown.go @@ -170,48 +170,6 @@ var blockTags = map[string]struct{}{ // // 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 }