func (r *Renderer) CaptionFigure(w io.Writer, figure *ast.CaptionFigure, entering bool) {
	fig := "<figure"
	if figure.HeadingID != "" {
		fig += ` id="` + figure.HeadingID + `">`
	} else {
		fig += ">"
	}
	r.OutOneOf(w, entering, fig, "\n</figure>\n")
}
CaptionFigure writes ast.CaptionFigure node
package html
import (
	"bytes"
	"fmt"
	"html"
	"io"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"github.com/gomarkdown/markdown/ast"
	"github.com/gomarkdown/markdown/parser"
)
type Flags int
var IDTag = "id"
const (
	FlagsNone               Flags = 0
	SkipHTML                Flags = 1 << iota  
	SkipImages                                 
	SkipLinks                                  
	Safelink                                   
	NofollowLinks                              
	NoreferrerLinks                            
	NoopenerLinks                              
	HrefTargetBlank                            
	CompletePage                               
	UseXHTML                                   
	FootnoteReturnLinks                        
	FootnoteNoHRTag                            
	Smartypants                                
	SmartypantsFractions                       
	SmartypantsDashes                          
	SmartypantsLatexDashes                     
	SmartypantsAngledQuotes                    
	SmartypantsQuotesNBSP                      
	TOC                                        
	LazyLoadImages                             
	CommonFlags Flags = Smartypants | SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes
)
var (
	htmlTagRe = regexp.MustCompile("(?i)^" + htmlTag)
)
const (
	htmlTag = "(?:" + openTag + "|" + closeTag + "|" + htmlComment + "|" +
		processingInstruction + "|" + declaration + "|" + cdata + ")"
	closeTag              = "</" + tagName + "\\s*[>]"
	openTag               = "<" + tagName + attribute + "*" + "\\s*/?>"
	attribute             = "(?:" + "\\s+" + attributeName + attributeValueSpec + "?)"
	attributeValue        = "(?:" + unquotedValue + "|" + singleQuotedValue + "|" + doubleQuotedValue + ")"
	attributeValueSpec    = "(?:" + "\\s*=" + "\\s*" + attributeValue + ")"
	attributeName         = "[a-zA-Z_:][a-zA-Z0-9:._-]*"
	cdata                 = "<!\\[CDATA\\[[\\s\\S]*?\\]\\]>"
	declaration           = "<![A-Z]+" + "\\s+[^>]*>"
	doubleQuotedValue     = "\"[^\"]*\""
	htmlComment           = "<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->"
	processingInstruction = "[<][?].*?[?][>]"
	singleQuotedValue     = "'[^']*'"
	tagName               = "[A-Za-z][A-Za-z0-9-]*"
	unquotedValue         = "[^\"'=<>`\\x00-\\x20]+"
)
type RenderNodeFunc func(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool)
type RendererOptions struct {
	AbsolutePrefix string
	FootnoteAnchorPrefix string
	FootnoteReturnLinkContents string
	CitationFormatString string
	HeadingIDPrefix string
	HeadingIDSuffix string
	Title string  
	CSS   string  
	Icon  string  
	Head  []byte  
	Flags Flags  
	RenderNodeHook RenderNodeFunc
	Comments [][]byte
	Generator string
}
type Renderer struct {
	opts RendererOptions
	closeTag string  
	headingIDs map[string]int
	lastOutputLen int
	DisableTags int
	sr *SPRenderer
	documentMatter ast.DocumentMatters  
}
var Escaper = [256][]byte{
	'&': []byte("&amp;"),
	'<': []byte("&lt;"),
	'>': []byte("&gt;"),
	'"': []byte("&quot;"),
}
func EscapeHTML(w io.Writer, d []byte) {
	var start, end int
	n := len(d)
	for end < n {
		escSeq := Escaper[d[end]]
		if escSeq != nil {
			w.Write(d[start:end])
			w.Write(escSeq)
			start = end + 1
		}
		end++
	}
	if start < n && end <= n {
		w.Write(d[start:end])
	}
}
func escLink(w io.Writer, text []byte) {
	unesc := html.UnescapeString(string(text))
	EscapeHTML(w, []byte(unesc))
}
func Escape(w io.Writer, text []byte) {
	esc := false
	for i := 0; i < len(text); i++ {
		if text[i] == '\\' {
			esc = !esc
		}
		if esc && text[i] == '\\' {
			continue
		}
		w.Write([]byte{text[i]})
	}
}
func NewRenderer(opts RendererOptions) *Renderer {
	closeTag := ">"
	if opts.Flags&UseXHTML != 0 {
		closeTag = " />"
	}
	if opts.FootnoteReturnLinkContents == "" {
		opts.FootnoteReturnLinkContents = `<sup>[return]</sup>`
	}
	if opts.CitationFormatString == "" {
		opts.CitationFormatString = `<sup>[%s]</sup>`
	}
	if opts.Generator == "" {
		opts.Generator = `  <meta name="GENERATOR" content="github.com/gomarkdown/markdown markdown processor for Go`
	}
	return &Renderer{
		opts: opts,
		closeTag:   closeTag,
		headingIDs: make(map[string]int),
		sr: NewSmartypantsRenderer(opts.Flags),
	}
}
func isHTMLTag(tag []byte, tagname string) bool {
	found, _ := findHTMLTagPos(tag, tagname)
	return found
}
// Look for a character, but ignore it when it's in any kind of quotes, it
// might be JavaScript
func skipUntilCharIgnoreQuotes(html []byte, start int, char byte) int {
	inSingleQuote := false
	inDoubleQuote := false
	inGraveQuote := false
	i := start
	for i < len(html) {
		switch {
		case html[i] == char && !inSingleQuote && !inDoubleQuote && !inGraveQuote:
			return i
		case html[i] == '\'':
			inSingleQuote = !inSingleQuote
		case html[i] == '"':
			inDoubleQuote = !inDoubleQuote
		case html[i] == '`':
			inGraveQuote = !inGraveQuote
		}
		i++
	}
	return start
}
func findHTMLTagPos(tag []byte, tagname string) (bool, int) {
	i := 0
	if i < len(tag) && tag[0] != '<' {
		return false, -1
	}
	i++
	i = skipSpace(tag, i)
	if i < len(tag) && tag[i] == '/' {
		i++
	}
	i = skipSpace(tag, i)
	j := 0
	for ; i < len(tag); i, j = i+1, j+1 {
		if j >= len(tagname) {
			break
		}
		if strings.ToLower(string(tag[i]))[0] != tagname[j] {
			return false, -1
		}
	}
	if i == len(tag) {
		return false, -1
	}
	rightAngle := skipUntilCharIgnoreQuotes(tag, i, '>')
	if rightAngle >= i {
		return true, rightAngle
	}
	return false, -1
}
func isRelativeLink(link []byte) (yes bool) {
	// a tag begin with '#'
	if link[0] == '#' {
		return true
	}
	// link begin with '/' but not ' 
	if len(link) >= 2 && link[0] == '/' && link[1] != '/' {
		return true
	}
	if len(link) == 1 && link[0] == '/' {
		return true
	}
	if bytes.HasPrefix(link, []byte("./")) {
		return true
	}
	if bytes.HasPrefix(link, []byte("../")) {
		return true
	}
	return false
}
func (r *Renderer) addAbsPrefix(link []byte) []byte {
	if r.opts.AbsolutePrefix != "" && isRelativeLink(link) && link[0] != '.' {
		newDest := r.opts.AbsolutePrefix
		if link[0] != '/' {
			newDest += "/"
		}
		newDest += string(link)
		return []byte(newDest)
	}
	return link
}
func appendLinkAttrs(attrs []string, flags Flags, link []byte) []string {
	if isRelativeLink(link) {
		return attrs
	}
	var val []string
	if flags&NofollowLinks != 0 {
		val = append(val, "nofollow")
	}
	if flags&NoreferrerLinks != 0 {
		val = append(val, "noreferrer")
	}
	if flags&NoopenerLinks != 0 {
		val = append(val, "noopener")
	}
	if flags&HrefTargetBlank != 0 {
		attrs = append(attrs, `target="_blank"`)
	}
	if len(val) == 0 {
		return attrs
	}
	attr := fmt.Sprintf("rel=%q", strings.Join(val, " "))
	return append(attrs, attr)
}
func isMailto(link []byte) bool {
	return bytes.HasPrefix(link, []byte("mailto:"))
}
func needSkipLink(flags Flags, dest []byte) bool {
	if flags&SkipLinks != 0 {
		return true
	}
	return flags&Safelink != 0 && !isSafeLink(dest) && !isMailto(dest)
}
func isSmartypantable(node ast.Node) bool {
	switch node.GetParent().(type) {
	case *ast.Link, *ast.CodeBlock, *ast.Code:
		return false
	}
	return true
}
func appendLanguageAttr(attrs []string, info []byte) []string {
	if len(info) == 0 {
		return attrs
	}
	endOfLang := bytes.IndexAny(info, "\t ")
	if endOfLang < 0 {
		endOfLang = len(info)
	}
	s := `class="language-` + string(info[:endOfLang]) + `"`
	return append(attrs, s)
}
func (r *Renderer) outTag(w io.Writer, name string, attrs []string) {
	s := name
	if len(attrs) > 0 {
		s += " " + strings.Join(attrs, " ")
	}
	io.WriteString(w, s+">")
	r.lastOutputLen = 1
}
func footnoteRef(prefix string, node *ast.Link) string {
	urlFrag := prefix + string(slugify(node.Destination))
	nStr := strconv.Itoa(node.NoteID)
	anchor := `<a href="#fn:` + urlFrag + `">` + nStr + `</a>`
	return `<sup class="footnote-ref" id="fnref:` + urlFrag + `">` + anchor + `</sup>`
}
func footnoteItem(prefix string, slug []byte) string {
	return `<li id="fn:` + prefix + string(slug) + `">`
}
func footnoteReturnLink(prefix, returnLink string, slug []byte) string {
	return ` <a class="footnote-return" href="#fnref:` + prefix + string(slug) + `">` + returnLink + `</a>`
}
func listItemOpenCR(listItem *ast.ListItem) bool {
	if ast.GetPrevNode(listItem) == nil {
		return false
	}
	ld := listItem.Parent.(*ast.List)
	return !ld.Tight && ld.ListFlags&ast.ListTypeDefinition == 0
}
func skipParagraphTags(para *ast.Paragraph) bool {
	parent := para.Parent
	grandparent := parent.GetParent()
	if grandparent == nil || !isList(grandparent) {
		return false
	}
	isParentTerm := isListItemTerm(parent)
	grandparentListData := grandparent.(*ast.List)
	tightOrTerm := grandparentListData.Tight || isParentTerm
	return tightOrTerm
}
func (r *Renderer) Out(w io.Writer, d []byte) {
	r.lastOutputLen = len(d)
	if r.DisableTags > 0 {
		d = htmlTagRe.ReplaceAll(d, []byte{})
	}
	w.Write(d)
}
func (r *Renderer) Outs(w io.Writer, s string) {
	r.lastOutputLen = len(s)
	if r.DisableTags > 0 {
		s = htmlTagRe.ReplaceAllString(s, "")
	}
	io.WriteString(w, s)
}
func (r *Renderer) CR(w io.Writer) {
	if r.lastOutputLen > 0 {
		r.Outs(w, "\n")
	}
}
var (
	openHTags  = []string{"<h1", "<h2", "<h3", "<h4", "<h5"}
	closeHTags = []string{"</h1>", "</h2>", "</h3>", "</h4>", "</h5>"}
)
func headingOpenTagFromLevel(level int) string {
	if level < 1 || level > 5 {
		return "<h6"
	}
	return openHTags[level-1]
}
func headingCloseTagFromLevel(level int) string {
	if level < 1 || level > 5 {
		return "</h6>"
	}
	return closeHTags[level-1]
}
func (r *Renderer) outHRTag(w io.Writer, attrs []string) {
	hr := TagWithAttributes("<hr", attrs)
	r.OutOneOf(w, r.opts.Flags&UseXHTML == 0, hr, "<hr />")
}
func (r *Renderer) Text(w io.Writer, text *ast.Text) {
	if r.opts.Flags&Smartypants != 0 {
		var tmp bytes.Buffer
		EscapeHTML(&tmp, text.Literal)
		r.sr.Process(w, tmp.Bytes())
	} else {
		_, parentIsLink := text.Parent.(*ast.Link)
		if parentIsLink {
			escLink(w, text.Literal)
		} else {
			EscapeHTML(w, text.Literal)
		}
	}
}
func (r *Renderer) HardBreak(w io.Writer, node *ast.Hardbreak) {
	r.OutOneOf(w, r.opts.Flags&UseXHTML == 0, "<br>", "<br />")
	r.CR(w)
}
func (r *Renderer) NonBlockingSpace(w io.Writer, node *ast.NonBlockingSpace) {
	r.Outs(w, "&nbsp;")
}
func (r *Renderer) OutOneOf(w io.Writer, outFirst bool, first string, second string) {
	if outFirst {
		r.Outs(w, first)
	} else {
		r.Outs(w, second)
	}
}
func (r *Renderer) OutOneOfCr(w io.Writer, outFirst bool, first string, second string) {
	if outFirst {
		r.CR(w)
		r.Outs(w, first)
	} else {
		r.Outs(w, second)
		r.CR(w)
	}
}
func (r *Renderer) HTMLSpan(w io.Writer, span *ast.HTMLSpan) {
	if r.opts.Flags&SkipHTML == 0 {
		r.Out(w, span.Literal)
	}
}
func (r *Renderer) linkEnter(w io.Writer, link *ast.Link) {
	attrs := link.AdditionalAttributes
	dest := link.Destination
	dest = r.addAbsPrefix(dest)
	var hrefBuf bytes.Buffer
	hrefBuf.WriteString("href=\"")
	escLink(&hrefBuf, dest)
	hrefBuf.WriteByte('"')
	attrs = append(attrs, hrefBuf.String())
	if link.NoteID != 0 {
		r.Outs(w, footnoteRef(r.opts.FootnoteAnchorPrefix, link))
		return
	}
	attrs = appendLinkAttrs(attrs, r.opts.Flags, dest)
	if len(link.Title) > 0 {
		var titleBuff bytes.Buffer
		titleBuff.WriteString("title=\"")
		EscapeHTML(&titleBuff, link.Title)
		titleBuff.WriteByte('"')
		attrs = append(attrs, titleBuff.String())
	}
	r.outTag(w, "<a", attrs)
}
func (r *Renderer) linkExit(w io.Writer, link *ast.Link) {
	if link.NoteID == 0 {
		r.Outs(w, "</a>")
	}
}
func (r *Renderer) Link(w io.Writer, link *ast.Link, entering bool) {
	if needSkipLink(r.opts.Flags, link.Destination) {
		r.OutOneOf(w, entering, "<tt>", "</tt>")
		return
	}
	if entering {
		r.linkEnter(w, link)
	} else {
		r.linkExit(w, link)
	}
}
func (r *Renderer) imageEnter(w io.Writer, image *ast.Image) {
	dest := image.Destination
	dest = r.addAbsPrefix(dest)
	if r.DisableTags == 0 {
		if r.opts.Flags&LazyLoadImages != 0 {
			r.Outs(w, `<img loading="lazy" src="`)
		} else {
			r.Outs(w, `<img src="`)
		}
		escLink(w, dest)
		r.Outs(w, `" alt="`)
	}
	r.DisableTags++
}
func (r *Renderer) imageExit(w io.Writer, image *ast.Image) {
	r.DisableTags--
	if r.DisableTags == 0 {
		if image.Title != nil {
			r.Outs(w, `" title="`)
			EscapeHTML(w, image.Title)
		}
		r.Outs(w, `" />`)
	}
}
// Image writes ast.Image node
func (r *Renderer) Image(w io.Writer, node *ast.Image, entering bool) {
	if entering {
		r.imageEnter(w, node)
	} else {
		r.imageExit(w, node)
	}
}
func (r *Renderer) paragraphEnter(w io.Writer, para *ast.Paragraph) {
	// TODO: untangle this clusterfuck about when the newlines need
	// to be added and when not.
	prev := ast.GetPrevNode(para)
	if prev != nil {
		switch prev.(type) {
		case *ast.HTMLBlock, *ast.List, *ast.Paragraph, *ast.Heading, *ast.CaptionFigure, *ast.CodeBlock, *ast.BlockQuote, *ast.Aside, *ast.HorizontalRule:
			r.CR(w)
		}
	}
	if prev == nil {
		_, isParentBlockQuote := para.Parent.(*ast.BlockQuote)
		if isParentBlockQuote {
			r.CR(w)
		}
		_, isParentAside := para.Parent.(*ast.Aside)
		if isParentAside {
			r.CR(w)
		}
	}
	tag := TagWithAttributes("<p", BlockAttrs(para))
	r.Outs(w, tag)
}
func (r *Renderer) paragraphExit(w io.Writer, para *ast.Paragraph) {
	r.Outs(w, "</p>")
	if !(isListItem(para.Parent) && ast.GetNextNode(para) == nil) {
		r.CR(w)
	}
}
// Paragraph writes ast.Paragraph node
func (r *Renderer) Paragraph(w io.Writer, para *ast.Paragraph, entering bool) {
	if skipParagraphTags(para) {
		return
	}
	if entering {
		r.paragraphEnter(w, para)
	} else {
		r.paragraphExit(w, para)
	}
}
// Code writes ast.Code node
func (r *Renderer) Code(w io.Writer, node *ast.Code) {
	r.Outs(w, "<code>")
	EscapeHTML(w, node.Literal)
	r.Outs(w, "</code>")
}
// HTMLBlock write ast.HTMLBlock node
func (r *Renderer) HTMLBlock(w io.Writer, node *ast.HTMLBlock) {
	if r.opts.Flags&SkipHTML != 0 {
		return
	}
	r.CR(w)
	r.Out(w, node.Literal)
	r.CR(w)
}
func (r *Renderer) headingEnter(w io.Writer, nodeData *ast.Heading) {
	var attrs []string
	var class string
	// TODO(miek): add helper functions for coalescing these classes.
	if nodeData.IsTitleblock {
		class = "title"
	}
	if nodeData.IsSpecial {
		if class != "" {
			class += " special"
		} else {
			class = "special"
		}
	}
	if class != "" {
		attrs = []string{`class="` + class + `"`}
	}
	ensureUniqueHeadingID := func(id string) string {
		for count, found := r.headingIDs[id]; found; count, found = r.headingIDs[id] {
			tmp := fmt.Sprintf("%s-%d", id, count+1)
			if _, tmpFound := r.headingIDs[tmp]; !tmpFound {
				r.headingIDs[id] = count + 1
				id = tmp
			} else {
				id = id + "-1"
			}
		}
		if _, found := r.headingIDs[id]; !found {
			r.headingIDs[id] = 0
		}
		return id
	}
	if nodeData.HeadingID != "" {
		id := ensureUniqueHeadingID(nodeData.HeadingID)
		if r.opts.HeadingIDPrefix != "" {
			id = r.opts.HeadingIDPrefix + id
		}
		if r.opts.HeadingIDSuffix != "" {
			id = id + r.opts.HeadingIDSuffix
		}
		attrID := `id="` + id + `"`
		attrs = append(attrs, attrID)
	}
	attrs = append(attrs, BlockAttrs(nodeData)...)
	r.CR(w)
	r.outTag(w, headingOpenTagFromLevel(nodeData.Level), attrs)
}
func (r *Renderer) headingExit(w io.Writer, heading *ast.Heading) {
	r.Outs(w, headingCloseTagFromLevel(heading.Level))
	if !(isListItem(heading.Parent) && ast.GetNextNode(heading) == nil) {
		r.CR(w)
	}
}
// Heading writes ast.Heading node
func (r *Renderer) Heading(w io.Writer, node *ast.Heading, entering bool) {
	if entering {
		r.headingEnter(w, node)
	} else {
		r.headingExit(w, node)
	}
}
// HorizontalRule writes ast.HorizontalRule node
func (r *Renderer) HorizontalRule(w io.Writer, node *ast.HorizontalRule) {
	r.CR(w)
	r.outHRTag(w, BlockAttrs(node))
	r.CR(w)
}
func (r *Renderer) listEnter(w io.Writer, nodeData *ast.List) {
	// TODO: attrs don't seem to be set
	var attrs []string
	if nodeData.IsFootnotesList {
		r.Outs(w, "\n<div class=\"footnotes\">\n\n")
		if r.opts.Flags&FootnoteNoHRTag == 0 {
			r.outHRTag(w, nil)
			r.CR(w)
		}
	}
	r.CR(w)
	if isListItem(nodeData.Parent) {
		grand := nodeData.Parent.GetParent()
		if isListTight(grand) {
			r.CR(w)
		}
	}
	openTag := "<ul"
	if nodeData.ListFlags&ast.ListTypeOrdered != 0 {
		if nodeData.Start > 0 {
			attrs = append(attrs, fmt.Sprintf(`start="%d"`, nodeData.Start))
		}
		openTag = "<ol"
	}
	if nodeData.ListFlags&ast.ListTypeDefinition != 0 {
		openTag = "<dl"
	}
	attrs = append(attrs, BlockAttrs(nodeData)...)
	r.outTag(w, openTag, attrs)
	r.CR(w)
}
func (r *Renderer) listExit(w io.Writer, list *ast.List) {
	closeTag := "</ul>"
	if list.ListFlags&ast.ListTypeOrdered != 0 {
		closeTag = "</ol>"
	}
	if list.ListFlags&ast.ListTypeDefinition != 0 {
		closeTag = "</dl>"
	}
	r.Outs(w, closeTag)
	parent := list.Parent
	switch parent.(type) {
	case *ast.ListItem:
		if ast.GetNextNode(list) != nil {
			r.CR(w)
		}
	case *ast.Document, *ast.BlockQuote, *ast.Aside:
		r.CR(w)
	}
	if list.IsFootnotesList {
		r.Outs(w, "\n</div>\n")
	}
}
func (r *Renderer) List(w io.Writer, list *ast.List, entering bool) {
	if entering {
		r.listEnter(w, list)
	} else {
		r.listExit(w, list)
	}
}
func (r *Renderer) listItemEnter(w io.Writer, listItem *ast.ListItem) {
	if listItemOpenCR(listItem) {
		r.CR(w)
	}
	if listItem.RefLink != nil {
		slug := slugify(listItem.RefLink)
		r.Outs(w, footnoteItem(r.opts.FootnoteAnchorPrefix, slug))
		return
	}
	openTag := "<li>"
	if listItem.ListFlags&ast.ListTypeDefinition != 0 {
		openTag = "<dd>"
	}
	if listItem.ListFlags&ast.ListTypeTerm != 0 {
		openTag = "<dt>"
	}
	r.Outs(w, openTag)
}
func (r *Renderer) listItemExit(w io.Writer, listItem *ast.ListItem) {
	if listItem.RefLink != nil && r.opts.Flags&FootnoteReturnLinks != 0 {
		slug := slugify(listItem.RefLink)
		prefix := r.opts.FootnoteAnchorPrefix
		link := r.opts.FootnoteReturnLinkContents
		s := footnoteReturnLink(prefix, link, slug)
		r.Outs(w, s)
	}
	closeTag := "</li>"
	if listItem.ListFlags&ast.ListTypeDefinition != 0 {
		closeTag = "</dd>"
	}
	if listItem.ListFlags&ast.ListTypeTerm != 0 {
		closeTag = "</dt>"
	}
	r.Outs(w, closeTag)
	r.CR(w)
}
func (r *Renderer) ListItem(w io.Writer, listItem *ast.ListItem, entering bool) {
	if entering {
		r.listItemEnter(w, listItem)
	} else {
		r.listItemExit(w, listItem)
	}
}
func (r *Renderer) EscapeHTMLCallouts(w io.Writer, d []byte) {
	ld := len(d)
Parse:
	for i := 0; i < ld; i++ {
		for _, comment := range r.opts.Comments {
			if !bytes.HasPrefix(d[i:], comment) {
				break
			}
			lc := len(comment)
			if i+lc < ld {
				if id, consumed := parser.IsCallout(d[i+lc:]); consumed > 0 {
					callout := &ast.Callout{ID: id}
					r.Callout(w, callout)
					i += consumed + lc - 1
					continue Parse
				}
			}
		}
		escSeq := Escaper[d[i]]
		if escSeq != nil {
			w.Write(escSeq)
		} else {
			w.Write([]byte{d[i]})
		}
	}
}
func (r *Renderer) CodeBlock(w io.Writer, codeBlock *ast.CodeBlock) {
	var attrs []string
	attrs = appendLanguageAttr(attrs, codeBlock.Info)
	attrs = append(attrs, BlockAttrs(codeBlock)...)
	r.CR(w)
	r.Outs(w, "<pre>")
	code := TagWithAttributes("<code", attrs)
	r.Outs(w, code)
	if r.opts.Comments != nil {
		r.EscapeHTMLCallouts(w, codeBlock.Literal)
	} else {
		EscapeHTML(w, codeBlock.Literal)
	}
	r.Outs(w, "</code>")
	r.Outs(w, "</pre>")
	if !isListItem(codeBlock.Parent) {
		r.CR(w)
	}
}
func (r *Renderer) Caption(w io.Writer, caption *ast.Caption, entering bool) {
	if entering {
		r.Outs(w, "<figcaption>")
		return
	}
	r.Outs(w, "</figcaption>")
}