package markdown // Callback functions for inline parsing. One such function is defined import ( "bytes" "fmt" "strings" "unicode/utf8" ) // Extensions is a bitmask of enabled Parser extensions. type Extensions int // Bit flags representing markdown parsing extensions. // Use | (or) to specify multiple extensions. const ( NoExtensions Extensions = 0 NoIntraEmphasis Extensions = 1 << iota // Ignore emphasis markers inside words Tables // Render tables FencedCode // Render fenced code blocks Autolink // Detect embedded URLs that are not explicitly marked Strikethrough // Strikethrough text using ~~test~~ LaxHTMLBlocks // Loosen up HTML block parsing rules SpaceHeadings // Be strict about prefix heading rules HardLineBreak // Translate newlines into line breaks TabSizeEight // Expand tabs to eight spaces instead of four Footnotes // Pandoc-style footnotes NoEmptyLineBeforeBlock // No need to insert an empty line to start a (code, quote, ordered list, unordered list) block HeadingIDs // specify heading IDs with {#id} Titleblock // Titleblock ala pandoc AutoHeadingIDs // Create the heading ID from the text BackslashLineBreak // Translate trailing backslashes into line breaks DefinitionLists // Render definition lists CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | Autolink | Strikethrough | SpaceHeadings | HeadingIDs | BackslashLineBreak | DefinitionLists ) // ListType contains bitwise or'ed flags for list and list item objects. type ListType int // These are the possible flag values for the ListItem renderer. // Multiple flag values may be ORed together. // These are mostly of interest if you are writing a new output format. const ( ListTypeOrdered ListType = 1 << iota ListTypeDefinition ListTypeTerm ListItemContainsBlock ListItemBeginningOfList // TODO: figure out if this is of any use now ListItemEndOfList ) // CellAlignFlags holds a type of alignment in a table cell. type CellAlignFlags int // These are the possible flag values for the table cell renderer. // Only a single one of these values will be used; they are not ORed together. // These are mostly of interest if you are writing a new output format. const ( TableAlignmentLeft CellAlignFlags = 1 << iota TableAlignmentRight TableAlignmentCenter = (TableAlignmentLeft | TableAlignmentRight) ) // The size of a tab stop. const ( TabSizeDefault = 4 TabSizeDouble = 8 ) // for each character that triggers a response when parsing inline data. type inlineParser func(p *Parser, data []byte, offset int) (int, *Node) // ReferenceOverrideFunc is expected to be called with a reference string and // return either a valid Reference type that the reference string maps to or // nil. If overridden is false, the default reference logic will be executed. // See the documentation in Options for more details on use-case. type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool) // Parser is a type that holds extensions and the runtime state used by // Parse, and the renderer. You can not use it directly, construct it with New. type Parser struct { // ReferenceOverride is an optional function callback that is called every // time a reference is resolved. It can be set before starting parsing. // // In Markdown, the link reference syntax can be made to resolve a link to // a reference instead of an inline URL, in one of the following ways: // // * [link text][refid] // * [refid][] // // Usually, the refid is defined at the bottom of the Markdown document. If // this override function is provided, the refid is passed to the override // function first, before consulting the defined refids at the bottom. If // the override function indicates an override did not occur, the refids at // the bottom will be used to fill in the link details. ReferenceOverride ReferenceOverrideFunc // after parsing, this is AST root of parsed markdown text Doc *Node extensions Extensions refs map[string]*reference inlineCallback [256]inlineParser nesting int maxNesting int insideLink bool // Footnotes need to be ordered as well as available to quickly check for // presence. If a ref is also a footnote, it's stored both in refs and here // in notes. Slice is nil if footnotes not enabled. notes []*reference tip *Node // = doc oldTip *Node lastMatchedContainer *Node // = doc allClosed bool } // NewParser creates a markdown parser with CommonExtensions. func NewParser() *Parser { return NewParserWithExtensions(CommonExtensions) } // NewParserWithExtensions creates a markdown parser with given extensions. // Before // for Run() to customize parser's behavior and the renderer. func NewParserWithExtensions(extension Extensions) *Parser { p := Parser{ refs: make(map[string]*reference), maxNesting: 16, insideLink: false, Doc: NewNode(&DocumentData{}), extensions: extension, allClosed: true, } p.tip = p.Doc p.oldTip = p.Doc p.lastMatchedContainer = p.Doc p.inlineCallback[' '] = maybeLineBreak p.inlineCallback['*'] = emphasis p.inlineCallback['_'] = emphasis if p.extensions&Strikethrough != 0 { p.inlineCallback['~'] = emphasis } p.inlineCallback['`'] = codeSpan p.inlineCallback['\n'] = lineBreak p.inlineCallback['['] = link p.inlineCallback['<'] = leftAngle p.inlineCallback['\\'] = escape p.inlineCallback['&'] = entity p.inlineCallback['!'] = maybeImage p.inlineCallback['^'] = maybeInlineFootnote if p.extensions&Autolink != 0 { p.inlineCallback['h'] = maybeAutoLink p.inlineCallback['m'] = maybeAutoLink p.inlineCallback['f'] = maybeAutoLink p.inlineCallback['H'] = maybeAutoLink p.inlineCallback['M'] = maybeAutoLink p.inlineCallback['F'] = maybeAutoLink } if p.extensions&Footnotes != 0 { p.notes = make([]*reference, 0) } return &p } func (p *Parser) getRef(refid string) (ref *reference, found bool) { if p.ReferenceOverride != nil { r, overridden := p.ReferenceOverride(refid) if overridden { if r == nil { return nil, false } return &reference{ link: []byte(r.Link), title: []byte(r.Title), noteID: 0, hasBlock: false, text: []byte(r.Text)}, true } } // refs are case insensitive ref, found = p.refs[strings.ToLower(refid)] return ref, found } func (p *Parser) finalize(block *Node) { above := block.Parent block.open = false p.tip = above } func (p *Parser) addChild(d NodeData, offset uint32) *Node { return p.addExistingChild(NewNode(d), offset) } func (p *Parser) addExistingChild(node *Node, offset uint32) *Node { for !p.tip.canContain(node.Data) { p.finalize(p.tip) } p.tip.AppendChild(node) p.tip = node return node } func (p *Parser) closeUnmatchedBlocks() { if !p.allClosed { for p.oldTip != p.lastMatchedContainer { parent := p.oldTip.Parent p.finalize(p.oldTip) p.oldTip = parent } p.allClosed = true } } // Reference represents the details of a link. // See the documentation in Options for more details on use-case. type Reference struct { // Link is usually the URL the reference points to. Link string // Title is the alternate text describing the link in more detail. Title string // Text is the optional text to override the ref with if the syntax used was // [refid][] Text string } // Render renders a parsed data in parser with a given renderer func (p *Parser) Render(renderer Renderer) []byte { var buf bytes.Buffer ast := p.Doc renderer.RenderHeader(&buf, ast) ast.Walk(func(node *Node, entering bool) WalkStatus { return renderer.RenderNode(&buf, node, entering) }) renderer.RenderFooter(&buf, ast) return buf.Bytes() } // Parse is an entry point to the parsing part of Blackfriday. It takes an // input markdown document and produces a syntax tree for its contents. This // tree can then be rendered with a default or custom renderer, or // analyzed/transformed by the caller to whatever non-standard needs they have. // The return value is the root node of the syntax tree. func (p *Parser) Parse(input []byte) *Node { p.block(input) // Walk the tree and finish up some of unfinished blocks for p.tip != nil { p.finalize(p.tip) } // Walk the tree again and process inline markdown in each block p.Doc.Walk(func(node *Node, entering bool) WalkStatus { switch node.Data.(type) { case *ParagraphData, *HeadingData, *TableCellData: p.inline(node, node.content) node.content = nil } return GoToNext }) p.parseRefsToAST() return p.Doc } func (p *Parser) parseRefsToAST() { if p.extensions&Footnotes == 0 || len(p.notes) == 0 { return } p.tip = p.Doc d := &ListData{ IsFootnotesList: true, ListFlags: ListTypeOrdered, } block := p.addBlock(d, nil) flags := ListItemBeginningOfList // Note: this loop is intentionally explicit, not range-form. This is // because the body of the loop will append nested footnotes to p.notes and // we need to process those late additions. Range form would only walk over // the fixed initial set. for i := 0; i < len(p.notes); i++ { ref := p.notes[i] p.addExistingChild(ref.footnote, 0) block := ref.footnote blockData := block.Data.(*ItemData) blockData.ListFlags = flags | ListTypeOrdered blockData.RefLink = ref.link if ref.hasBlock { flags |= ListItemContainsBlock p.block(ref.title) } else { p.inline(block, ref.title) } flags &^= ListItemBeginningOfList | ListItemContainsBlock } above := block.Parent finalizeList(block, d) p.tip = above block.Walk(func(node *Node, entering bool) WalkStatus { switch node.Data.(type) { case *ParagraphData, *HeadingData: p.inline(node, node.content) node.content = nil } return GoToNext }) } // // Link references // // This section implements support for references that (usually) appear // as footnotes in a document, and can be referenced anywhere in the document. // The basic format is: // // [1]: http://www.google.com/ "Google" // [2]: http://www.github.com/ "Github" // // Anywhere in the document, the reference can be linked by referring to its // label, i.e., 1 and 2 in this example, as in: // // This library is hosted on [Github][2], a git hosting site. // // Actual footnotes as specified in Pandoc and supported by some other Markdown // libraries such as php-markdown are also taken care of. They look like this: // // This sentence needs a bit of further explanation.[^note] // // [^note]: This is the explanation. // // Footnotes should be placed at the end of the document in an ordered list. // Inline footnotes such as: // // Inline footnotes^[Not supported.] also exist. // // are not yet supported. // reference holds all information necessary for a reference-style links or // footnotes. // // Consider this markdown with reference-style links: // // [link][ref] // // [ref]: /url/ "tooltip title" // // It will be ultimately converted to this HTML: // //
// // And a reference structure will be populated as follows: // // p.refs["ref"] = &reference{ // link: "/url/", // title: "tooltip title", // } // // Alternatively, reference can contain information about a footnote. Consider // this markdown: // // Text needing a footnote.[^a] // // [^a]: This is the note // // A reference structure will be populated as follows: // // p.refs["a"] = &reference{ // link: "a", // title: "This is the note", // noteID: