all repos — grayfriday @ 747587a52d3bf20215a1ea2d40831b367acac37b

blackfriday fork with a few changes

Fix all headings wrongly referred to as headers

I've left test cases alone since can't lean on the compiler for
crosschecking there.

Fixes #330.
Vytautas Ĺ altenis vytas@rtfb.lt
Sun, 12 Feb 2017 19:00:18 +0200
commit

747587a52d3bf20215a1ea2d40831b367acac37b

parent

ad7f7c56d58a2c7f75a14cdcaa8b8acd5dc4f141

6 files changed, 89 insertions(+), 89 deletions(-)

jump to
M README.mdREADME.md

@@ -224,7 +224,7 @@ are a few of note:

* [github_flavored_markdown](https://godoc.org/github.com/shurcooL/github_flavored_markdown): provides a GitHub Flavored Markdown renderer with fenced code block - highlighting, clickable header anchor links. + highlighting, clickable heading anchor links. It's not customizable, and its goal is to produce HTML output equivalent to the [GitHub Markdown API endpoint](https://developer.github.com/v3/markdown/#render-a-markdown-document-in-raw-mode),
M block.goblock.go

@@ -43,14 +43,14 @@ p.nesting++

// parse out one block-level construct at a time for len(data) > 0 { - // prefixed header: + // prefixed heading: // - // # Header 1 - // ## Header 2 + // # Heading 1 + // ## Heading 2 // ... - // ###### Header 6 - if p.isPrefixHeader(data) { - data = data[p.prefixHeader(data):] + // ###### Heading 6 + if p.isPrefixHeading(data) { + data = data[p.prefixHeading(data):] continue }

@@ -190,7 +190,7 @@ }

} // anything else must look like a normal paragraph - // note: this finds underlined headers, too + // note: this finds underlined headings, too data = data[p.paragraph(data):] }

@@ -204,12 +204,12 @@ container.content = content

return container } -func (p *parser) isPrefixHeader(data []byte) bool { +func (p *parser) isPrefixHeading(data []byte) bool { if data[0] != '#' { return false } - if p.flags&SpaceHeaders != 0 { + if p.flags&SpaceHeadings != 0 { level := 0 for level < 6 && level < len(data) && data[level] == '#' { level++

@@ -221,7 +221,7 @@ }

return true } -func (p *parser) prefixHeader(data []byte) int { +func (p *parser) prefixHeading(data []byte) int { level := 0 for level < 6 && level < len(data) && data[level] == '#' { level++

@@ -230,14 +230,14 @@ i := skipChar(data, level, ' ')

end := skipUntilChar(data, i, '\n') skip := end id := "" - if p.flags&HeaderIDs != 0 { + if p.flags&HeadingIDs != 0 { j, k := 0, 0 - // find start/end of header id + // find start/end of heading id for j = i; j < end-1 && (data[j] != '{' || data[j+1] != '#'); j++ { } for k = j + 1; k < end && data[k] != '}'; k++ { } - // extract header id iff found + // extract heading id iff found if j < end && k < end { id = string(data[j+2 : k]) end = j

@@ -257,18 +257,18 @@ for end > 0 && data[end-1] == ' ' {

end-- } if end > i { - if id == "" && p.flags&AutoHeaderIDs != 0 { + if id == "" && p.flags&AutoHeadingIDs != 0 { id = sanitized_anchor_name.Create(string(data[i:end])) } - block := p.addBlock(Header, data[i:end]) - block.HeaderID = id + block := p.addBlock(Heading, data[i:end]) + block.HeadingID = id block.Level = level } return skip } -func (p *parser) isUnderlinedHeader(data []byte) int { - // test of level 1 header +func (p *parser) isUnderlinedHeading(data []byte) int { + // test of level 1 heading if data[0] == '=' { i := skipChar(data, 1, '=') i = skipChar(data, i, ' ')

@@ -278,7 +278,7 @@ }

return 0 } - // test of level 2 header + // test of level 2 heading if data[0] == '-' { i := skipChar(data, 1, '-') i = skipChar(data, i, ' ')

@@ -308,7 +308,7 @@ data = bytes.Join(splitData[0:i], []byte("\n"))

consumed := len(data) data = bytes.TrimPrefix(data, []byte("% ")) data = bytes.Replace(data, []byte("\n% "), []byte("\n"), -1) - block := p.addBlock(Header, data) + block := p.addBlock(Heading, data) block.Level = 1 block.IsTitleblock = true

@@ -1301,9 +1301,9 @@ if sublist == 0 {

sublist = raw.Len() } - // is this a nested prefix header? - case p.isPrefixHeader(chunk): - // if the header is not indented, it is not nested in the list + // is this a nested prefix heading? + case p.isPrefixHeading(chunk): + // if the heading is not indented, it is not nested in the list // and thus ends the list if containsBlankLine && indent < 4 { *flags |= ListItemEndOfList

@@ -1445,9 +1445,9 @@ p.renderParagraph(data[:i])

return i + n } - // an underline under some text marks a header, so our paragraph ended on prev line + // an underline under some text marks a heading, so our paragraph ended on prev line if i > 0 { - if level := p.isUnderlinedHeader(current); level > 0 { + if level := p.isUnderlinedHeading(current); level > 0 { // render the paragraph p.renderParagraph(data[:prev])

@@ -1461,13 +1461,13 @@ eol--

} id := "" - if p.flags&AutoHeaderIDs != 0 { + if p.flags&AutoHeadingIDs != 0 { id = sanitized_anchor_name.Create(string(data[prev:eol])) } - block := p.addBlock(Header, data[prev:eol]) + block := p.addBlock(Heading, data[prev:eol]) block.Level = level - block.HeaderID = id + block.HeadingID = id // find the end of the underline for i < len(data) && data[i] != '\n' {

@@ -1486,8 +1486,8 @@ return i

} } - // if there's a prefixed header or a horizontal rule after this, paragraph is over - if p.isPrefixHeader(current) || p.isHRule(current) { + // if there's a prefixed heading or a horizontal rule after this, paragraph is over + if p.isPrefixHeading(current) || p.isHRule(current) { p.renderParagraph(data[:i]) return i }
M block_test.goblock_test.go

@@ -144,7 +144,7 @@ "* List\n * Nested list\n # Nested header\n",

"<ul>\n<li><p>List</p>\n\n<ul>\n<li><p>Nested list</p>\n\n" + "<h1>Nested header</h1></li>\n</ul></li>\n</ul>\n", } - doTestsBlock(t, tests, SpaceHeaders) + doTestsBlock(t, tests, SpaceHeadings) } func TestPrefixHeaderIdExtension(t *testing.T) {

@@ -204,7 +204,7 @@ "* List\n * Nested list\n # Nested header {#someid}\n",

"<ul>\n<li><p>List</p>\n\n<ul>\n<li><p>Nested list</p>\n\n" + "<h1 id=\"someid\">Nested header</h1></li>\n</ul></li>\n</ul>\n", } - doTestsBlock(t, tests, HeaderIDs) + doTestsBlock(t, tests, HeadingIDs) } func TestPrefixHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) {

@@ -248,12 +248,12 @@ "<h1 id=\"PRE:someid:POST\">Nested header</h1></li>\n</ul></li>\n</ul>\n",

} parameters := HTMLRendererParameters{ - HeaderIDPrefix: "PRE:", - HeaderIDSuffix: ":POST", + HeadingIDPrefix: "PRE:", + HeadingIDSuffix: ":POST", } doTestsParam(t, tests, TestParams{ - Options: Options{Extensions: HeaderIDs}, + Options: Options{Extensions: HeadingIDs}, HTMLFlags: UseXHTML, HTMLRendererParameters: parameters, })

@@ -307,7 +307,7 @@

"# Header\n\n# Header 1\n\n# Header\n\n# Header", "<h1 id=\"header\">Header</h1>\n\n<h1 id=\"header-1\">Header 1</h1>\n\n<h1 id=\"header-1-1\">Header</h1>\n\n<h1 id=\"header-1-2\">Header</h1>\n", } - doTestsBlock(t, tests, AutoHeaderIDs) + doTestsBlock(t, tests, AutoHeadingIDs) } func TestPrefixAutoHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) {

@@ -360,12 +360,12 @@ "<h1 id=\"PRE:header:POST\">Header</h1>\n\n<h1 id=\"PRE:header-1:POST\">Header 1</h1>\n\n<h1 id=\"PRE:header-1-1:POST\">Header</h1>\n\n<h1 id=\"PRE:header-1-2:POST\">Header</h1>\n",

} parameters := HTMLRendererParameters{ - HeaderIDPrefix: "PRE:", - HeaderIDSuffix: ":POST", + HeadingIDPrefix: "PRE:", + HeadingIDSuffix: ":POST", } doTestsParam(t, tests, TestParams{ - Options: Options{Extensions: AutoHeaderIDs}, + Options: Options{Extensions: AutoHeadingIDs}, HTMLFlags: UseXHTML, HTMLRendererParameters: parameters, })

@@ -376,7 +376,7 @@ var tests = []string{

"# Header\n\n# Header {#header}\n\n# Header 1", "<h1 id=\"header\">Header</h1>\n\n<h1 id=\"header-1\">Header</h1>\n\n<h1 id=\"header-1-1\">Header 1</h1>\n", } - doTestsBlock(t, tests, AutoHeaderIDs|HeaderIDs) + doTestsBlock(t, tests, AutoHeadingIDs|HeadingIDs) } func TestUnderlineHeaders(t *testing.T) {

@@ -476,7 +476,7 @@

"Header 1\n========\n\nHeader 1\n========\n", "<h1 id=\"header-1\">Header 1</h1>\n\n<h1 id=\"header-1-1\">Header 1</h1>\n", } - doTestsBlock(t, tests, AutoHeaderIDs) + doTestsBlock(t, tests, AutoHeadingIDs) } func TestHorizontalRule(t *testing.T) {
M html.gohtml.go

@@ -80,11 +80,11 @@ // Show this text inside the <a> tag for a footnote return link, if the

// HTML_FOOTNOTE_RETURN_LINKS flag is enabled. If blank, the string // <sup>[return]</sup> is used. FootnoteReturnLinkContents string - // If set, add this text to the front of each Header ID, to ensure + // If set, add this text to the front of each Heading ID, to ensure // uniqueness. - HeaderIDPrefix string - // If set, add this text to the back of each Header ID, to ensure uniqueness. - HeaderIDSuffix string + HeadingIDPrefix string + // If set, add this text to the back of each Heading ID, to ensure uniqueness. + HeadingIDSuffix string Title string // Document title (used if CompletePage is set) CSS string // Optional CSS file URL (used if CompletePage is set)

@@ -101,8 +101,8 @@ HTMLRendererParameters

closeTag string // how to end singleton tags: either " />" or ">" - // Track header IDs to prevent ID collision in a single generation. - headerIDs map[string]int + // Track heading IDs to prevent ID collision in a single generation. + headingIDs map[string]int lastOutputLen int disableTags int

@@ -131,8 +131,8 @@

return &HTMLRenderer{ HTMLRendererParameters: params, - closeTag: closeTag, - headerIDs: make(map[string]int), + closeTag: closeTag, + headingIDs: make(map[string]int), sr: NewSmartypantsRenderer(params.Flags), }

@@ -238,20 +238,20 @@

return false } -func (r *HTMLRenderer) ensureUniqueHeaderID(id string) string { - for count, found := r.headerIDs[id]; found; count, found = r.headerIDs[id] { +func (r *HTMLRenderer) ensureUniqueHeadingID(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.headerIDs[tmp]; !tmpFound { - r.headerIDs[id] = count + 1 + if _, tmpFound := r.headingIDs[tmp]; !tmpFound { + r.headingIDs[id] = count + 1 id = tmp } else { id = id + "-1" } } - if _, found := r.headerIDs[id]; !found { - r.headerIDs[id] = 0 + if _, found := r.headingIDs[id]; !found { + r.headingIDs[id] = 0 } return id

@@ -457,7 +457,7 @@ footnotesDivBytes = []byte("\n<div class=\"footnotes\">\n\n")

footnotesCloseDivBytes = []byte("\n</div>\n") ) -func headerTagsFromLevel(level int) ([]byte, []byte) { +func headingTagsFromLevel(level int) ([]byte, []byte) { switch level { case 1: return h1Tag, h1CloseTag

@@ -619,7 +619,7 @@ // TODO: untangle this clusterfuck about when the newlines need

// to be added and when not. if node.Prev != nil { switch node.Prev.Type { - case HTMLBlock, List, Paragraph, Header, CodeBlock, BlockQuote, HorizontalRule: + case HTMLBlock, List, Paragraph, Heading, CodeBlock, BlockQuote, HorizontalRule: r.cr(w) } }

@@ -648,19 +648,19 @@ }

r.cr(w) r.out(w, node.Literal) r.cr(w) - case Header: - openTag, closeTag := headerTagsFromLevel(node.Level) + case Heading: + openTag, closeTag := headingTagsFromLevel(node.Level) if entering { if node.IsTitleblock { attrs = append(attrs, `class="title"`) } - if node.HeaderID != "" { - id := r.ensureUniqueHeaderID(node.HeaderID) - if r.HeaderIDPrefix != "" { - id = r.HeaderIDPrefix + id + if node.HeadingID != "" { + id := r.ensureUniqueHeadingID(node.HeadingID) + if r.HeadingIDPrefix != "" { + id = r.HeadingIDPrefix + id } - if r.HeaderIDSuffix != "" { - id = id + r.HeaderIDSuffix + if r.HeadingIDSuffix != "" { + id = id + r.HeadingIDSuffix } attrs = append(attrs, fmt.Sprintf(`id="%s"`, id)) }

@@ -870,15 +870,15 @@

func (r *HTMLRenderer) writeTOC(w *bytes.Buffer, ast *Node) { buf := bytes.Buffer{} - inHeader := false + inHeading := false tocLevel := 0 - headerCount := 0 + headingCount := 0 ast.Walk(func(node *Node, entering bool) WalkStatus { - if node.Type == Header && !node.HeaderData.IsTitleblock { - inHeader = entering + if node.Type == Heading && !node.HeadingData.IsTitleblock { + inHeading = entering if entering { - node.HeaderID = fmt.Sprintf("toc_%d", headerCount) + node.HeadingID = fmt.Sprintf("toc_%d", headingCount) if node.Level == tocLevel { buf.WriteString("</li>\n\n<li>") } else if node.Level < tocLevel {

@@ -894,15 +894,15 @@ buf.WriteString("\n<ul>\n<li>")

} } - fmt.Fprintf(&buf, `<a href="#toc_%d">`, headerCount) - headerCount++ + fmt.Fprintf(&buf, `<a href="#toc_%d">`, headingCount) + headingCount++ } else { buf.WriteString("</a>") } return GoToNext } - if inHeader { + if inHeading { return r.RenderNode(&buf, node, entering) }
M markdown.gomarkdown.go

@@ -36,14 +36,14 @@ 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 - SpaceHeaders // Be strict about prefix header 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 - HeaderIDs // specify header IDs with {#id} + HeadingIDs // specify heading IDs with {#id} Titleblock // Titleblock ala pandoc - AutoHeaderIDs // Create the header ID from the text + AutoHeadingIDs // Create the heading ID from the text BackslashLineBreak // Translate trailing backslashes into line breaks DefinitionLists // Render definition lists

@@ -51,7 +51,7 @@ CommonHTMLFlags HTMLFlags = UseXHTML | Smartypants |

SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | - Autolink | Strikethrough | SpaceHeaders | HeaderIDs | + Autolink | Strikethrough | SpaceHeadings | HeadingIDs | BackslashLineBreak | DefinitionLists )

@@ -310,9 +310,9 @@ // * Autolinking

// // * Strikethrough support // -// * Strict header parsing +// * Strict heading parsing // -// * Custom Header IDs +// * Custom Heading IDs func MarkdownCommon(input []byte) []byte { // set up the HTML renderer renderer := NewHTMLRenderer(HTMLRendererParameters{

@@ -392,7 +392,7 @@ p.finalize(p.tip)

} // Walk the tree again and process inline markdown in each block p.doc.Walk(func(node *Node, entering bool) WalkStatus { - if node.Type == Paragraph || node.Type == Header || node.Type == TableCell { + if node.Type == Paragraph || node.Type == Heading || node.Type == TableCell { p.inline(node, node.content) node.content = nil }

@@ -433,7 +433,7 @@ above := block.Parent

finalizeList(block) p.tip = above block.Walk(func(node *Node, entering bool) WalkStatus { - if node.Type == Paragraph || node.Type == Header { + if node.Type == Paragraph || node.Type == Heading { p.inline(node, node.content) node.content = nil }
M node.gonode.go

@@ -17,7 +17,7 @@ BlockQuote

List Item Paragraph - Header + Heading HorizontalRule Emph Strong

@@ -44,7 +44,7 @@ BlockQuote: "BlockQuote",

List: "List", Item: "Item", Paragraph: "Paragraph", - Header: "Header", + Heading: "Heading", HorizontalRule: "HorizontalRule", Emph: "Emph", Strong: "Strong",

@@ -102,10 +102,10 @@ IsHeader bool // This tells if it's under the header row

Align CellAlignFlags // This holds the value for align attribute } -// HeaderData contains fields relevant to a Header node type. -type HeaderData struct { +// HeadingData contains fields relevant to a Heading node type. +type HeadingData struct { Level int // This holds the heading level number - HeaderID string // This might hold header ID, if present + HeadingID string // This might hold heading ID, if present IsTitleblock bool // Specifies whether it's a title block }

@@ -122,7 +122,7 @@ Next *Node // Next sibling; nil if it's the last child

Literal []byte // Text contents of the leaf nodes - HeaderData // Populated if Type is Header + HeadingData // Populated if Type is Heading ListData // Populated if Type is List CodeBlockData // Populated if Type is CodeBlock LinkData // Populated if Type is Link

@@ -211,7 +211,7 @@ case Item:

fallthrough case Paragraph: fallthrough - case Header: + case Heading: fallthrough case Emph: fallthrough