From 747587a52d3bf20215a1ea2d40831b367acac37b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vytautas=20=C5=A0altenis?= Date: Sun, 12 Feb 2017 19:00:18 +0200 Subject: [PATCH] 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. --- README.md | 2 +- block.go | 60 +++++++++++++++++++++++------------------------ block_test.go | 22 +++++++++--------- html.go | 64 +++++++++++++++++++++++++-------------------------- markdown.go | 16 ++++++------- node.go | 14 +++++------ 6 files changed, 89 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index b025f1c..0ee4a40 100644 --- a/README.md +++ b/README.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), diff --git a/block.go b/block.go index 9d3a003..db7ff22 100644 --- a/block.go +++ b/block.go @@ -43,14 +43,14 @@ func (p *parser) block(data []byte) { // 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 @@ func (p *parser) block(data []byte) { } // 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 @@ func (p *parser) addBlock(typ NodeType, content []byte) *Node { 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 @@ func (p *parser) isPrefixHeader(data []byte) bool { 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 @@ func (p *parser) prefixHeader(data []byte) int { 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 @@ func (p *parser) prefixHeader(data []byte) int { 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 @@ func (p *parser) isUnderlinedHeader(data []byte) int { 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 @@ func (p *parser) titleBlock(data []byte, doRender bool) int { 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 @@ gatherlines: 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 @@ func (p *parser) paragraph(data []byte) int { 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 @@ func (p *parser) paragraph(data []byte) int { } 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 @@ func (p *parser) paragraph(data []byte) int { } } - // 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 } diff --git a/block_test.go b/block_test.go index 0a944a1..b2d86f3 100644 --- a/block_test.go +++ b/block_test.go @@ -144,7 +144,7 @@ func TestPrefixHeaderSpaceExtension(t *testing.T) { "\n", } - doTestsBlock(t, tests, SpaceHeaders) + doTestsBlock(t, tests, SpaceHeadings) } func TestPrefixHeaderIdExtension(t *testing.T) { @@ -204,7 +204,7 @@ func TestPrefixHeaderIdExtension(t *testing.T) { "\n", } - doTestsBlock(t, tests, HeaderIDs) + doTestsBlock(t, tests, HeadingIDs) } func TestPrefixHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) { @@ -248,12 +248,12 @@ func TestPrefixHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) { } 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 @@ func TestPrefixAutoHeaderIdExtension(t *testing.T) { "# Header\n\n# Header 1\n\n# Header\n\n# Header", "

Header

\n\n

Header 1

\n\n

Header

\n\n

Header

\n", } - doTestsBlock(t, tests, AutoHeaderIDs) + doTestsBlock(t, tests, AutoHeadingIDs) } func TestPrefixAutoHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) { @@ -360,12 +360,12 @@ func TestPrefixAutoHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) { } 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 @@ func TestPrefixMultipleHeaderExtensions(t *testing.T) { "# Header\n\n# Header {#header}\n\n# Header 1", "

Header

\n\n

Header

\n\n

Header 1

\n", } - doTestsBlock(t, tests, AutoHeaderIDs|HeaderIDs) + doTestsBlock(t, tests, AutoHeadingIDs|HeadingIDs) } func TestUnderlineHeaders(t *testing.T) { @@ -476,7 +476,7 @@ func TestUnderlineHeadersAutoIDs(t *testing.T) { "Header 1\n========\n\nHeader 1\n========\n", "

Header 1

\n\n

Header 1

\n", } - doTestsBlock(t, tests, AutoHeaderIDs) + doTestsBlock(t, tests, AutoHeadingIDs) } func TestHorizontalRule(t *testing.T) { diff --git a/html.go b/html.go index ef5cce5..a47b740 100644 --- a/html.go +++ b/html.go @@ -80,11 +80,11 @@ type HTMLRendererParameters struct { // HTML_FOOTNOTE_RETURN_LINKS flag is enabled. If blank, the string // [return] 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 @@ type HTMLRenderer struct { 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 @@ func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer { 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 @@ func isRelativeLink(link []byte) (yes bool) { 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 @@ var ( footnotesCloseDivBytes = []byte("\n\n") ) -func headerTagsFromLevel(level int) ([]byte, []byte) { +func headingTagsFromLevel(level int) ([]byte, []byte) { switch level { case 1: return h1Tag, h1CloseTag @@ -619,7 +619,7 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt // 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 @@ func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkSt 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) writeDocumentHeader(w *bytes.Buffer) { 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("\n\n
  • ") } else if node.Level < tocLevel { @@ -894,15 +894,15 @@ func (r *HTMLRenderer) writeTOC(w *bytes.Buffer, ast *Node) { } } - fmt.Fprintf(&buf, ``, headerCount) - headerCount++ + fmt.Fprintf(&buf, ``, headingCount) + headingCount++ } else { buf.WriteString("") } return GoToNext } - if inHeader { + if inHeading { return r.RenderNode(&buf, node, entering) } diff --git a/markdown.go b/markdown.go index 9c3fdb0..d8bd36e 100644 --- a/markdown.go +++ b/markdown.go @@ -36,14 +36,14 @@ const ( 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 @@ const ( SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | - Autolink | Strikethrough | SpaceHeaders | HeaderIDs | + Autolink | Strikethrough | SpaceHeadings | HeadingIDs | BackslashLineBreak | DefinitionLists ) @@ -310,9 +310,9 @@ func MarkdownBasic(input []byte) []byte { // // * 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 @@ func Parse(input []byte, opts Options) *Node { } // 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 @@ func (p *parser) parseRefsToAST() { 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 } diff --git a/node.go b/node.go index 8f10698..51b9e8c 100644 --- a/node.go +++ b/node.go @@ -17,7 +17,7 @@ const ( List Item Paragraph - Header + Heading HorizontalRule Emph Strong @@ -44,7 +44,7 @@ var nodeTypeNames = []string{ List: "List", Item: "Item", Paragraph: "Paragraph", - Header: "Header", + Heading: "Heading", HorizontalRule: "HorizontalRule", Emph: "Emph", Strong: "Strong", @@ -102,10 +102,10 @@ type TableCellData struct { 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 @@ type Node struct { 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 @@ func (n *Node) isContainer() bool { fallthrough case Paragraph: fallthrough - case Header: + case Heading: fallthrough case Emph: fallthrough