Continue with TOC integration and page refactor. Updated a few tests to match new generated output.

This commit is contained in:
spf13 2014-01-28 23:11:05 -05:00
parent f45c6bc38a
commit 1da3fd039a
2 changed files with 411 additions and 354 deletions

View file

@ -38,6 +38,7 @@ type Page struct {
rawContent []byte rawContent []byte
Content template.HTML Content template.HTML
Summary template.HTML Summary template.HTML
TableOfContents template.HTML
Truncated bool Truncated bool
plain string // TODO should be []byte plain string // TODO should be []byte
Params map[string]interface{} Params map[string]interface{}
@ -75,7 +76,7 @@ type Pages []*Page
func (p *Page) Plain() string { func (p *Page) Plain() string {
if len(p.plain) == 0 { if len(p.plain) == 0 {
p.plain = StripHTML(StripShortcodes(string(p.rawContent))) p.plain = StripHTML(StripShortcodes(string(p.renderBytes(p.rawContent))))
} }
return p.plain return p.plain
} }
@ -96,6 +97,10 @@ func (p *Page) setSummary() {
} }
} }
func stripEmptyNav(in []byte) []byte {
return bytes.Replace(in, []byte("<nav>\n</nav>\n\n"), []byte(``), -1)
}
func bytesToHTML(b []byte) template.HTML { func bytesToHTML(b []byte) template.HTML {
return template.HTML(string(b)) return template.HTML(string(b))
} }
@ -104,16 +109,27 @@ func (p *Page) renderBytes(content []byte) []byte {
return renderBytes(content, p.guessMarkupType()) return renderBytes(content, p.guessMarkupType())
} }
func (p *Page) renderString(content string) []byte { func (p *Page) renderContent(content []byte) []byte {
return renderBytes([]byte(content), p.guessMarkupType()) return renderBytesWithTOC(content, p.guessMarkupType())
}
func renderBytesWithTOC(content []byte, pagefmt string) []byte {
switch pagefmt {
default:
return markdownRenderWithTOC(content)
case "markdown":
return markdownRenderWithTOC(content)
case "rst":
return []byte(getRstContent(content))
}
} }
func renderBytes(content []byte, pagefmt string) []byte { func renderBytes(content []byte, pagefmt string) []byte {
switch pagefmt { switch pagefmt {
default: default:
return blackfriday.MarkdownCommon(content) return markdownRender(content)
case "markdown": case "markdown":
return blackfriday.MarkdownCommon(content) return markdownRender(content)
case "rst": case "rst":
return []byte(getRstContent(content)) return []byte(getRstContent(content))
} }
@ -553,7 +569,9 @@ func (page *Page) Convert() error {
markupType := page.guessMarkupType() markupType := page.guessMarkupType()
switch markupType { switch markupType {
case "markdown", "rst": case "markdown", "rst":
page.Content = bytesToHTML(page.renderString(string(RemoveSummaryDivider(page.rawContent)))) tmpContent, tmpTableOfContents := extractTOC(page.renderContent(RemoveSummaryDivider(page.rawContent)))
page.Content = bytesToHTML(tmpContent)
page.TableOfContents = bytesToHTML(tmpTableOfContents)
case "html": case "html":
page.Content = bytesToHTML(page.rawContent) page.Content = bytesToHTML(page.rawContent)
default: default:
@ -562,19 +580,58 @@ func (page *Page) Convert() error {
return nil return nil
} }
// Lazily generate the TOC func markdownRender(content []byte) []byte {
func (page *Page) TableOfContents() template.HTML { htmlFlags := 0
return tableOfContentsFromBytes([]byte(page.Content)) htmlFlags |= blackfriday.HTML_SKIP_SCRIPT
htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
renderer := blackfriday.HtmlRenderer(htmlFlags, "", "")
return blackfriday.Markdown(content, renderer, 0)
} }
func tableOfContentsFromBytes(content []byte) template.HTML { func markdownRenderWithTOC(content []byte) []byte {
htmlFlags := 0 htmlFlags := 0
htmlFlags |= blackfriday.HTML_SKIP_SCRIPT htmlFlags |= blackfriday.HTML_SKIP_SCRIPT
htmlFlags |= blackfriday.HTML_TOC htmlFlags |= blackfriday.HTML_TOC
htmlFlags |= blackfriday.HTML_OMIT_CONTENTS htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
renderer := blackfriday.HtmlRenderer(htmlFlags, "", "") renderer := blackfriday.HtmlRenderer(htmlFlags, "", "")
return template.HTML(string(blackfriday.Markdown(RemoveSummaryDivider(content), renderer, 0))) return blackfriday.Markdown(content, renderer, 0)
}
func extractTOC(content []byte) (newcontent []byte, toc []byte) {
origContent := make([]byte, len(content))
copy(origContent, content)
first := []byte(`<nav>
<ul>`)
last := []byte(`</ul>
</nav>`)
replacement := []byte(`<nav id="TableOfContents">
<ul>`)
startOfTOC := bytes.Index(content, first)
peekEnd := len(content)
if peekEnd > 70+startOfTOC {
peekEnd = 70 + startOfTOC
}
if startOfTOC < 0 {
return stripEmptyNav(content), toc
}
// Need to peek ahead to see if this nav element is actually the right one.
correctNav := bytes.Index(content[startOfTOC:peekEnd], []byte(`#toc_0`))
if correctNav < 0 { // no match found
return content, toc
}
lengthOfTOC := bytes.Index(content[startOfTOC:], last) + len(last)
endOfTOC := startOfTOC + lengthOfTOC
newcontent = append(content[:startOfTOC], content[endOfTOC:]...)
toc = append(replacement, origContent[startOfTOC+len(first):endOfTOC]...)
return
} }
func ReaderToBytes(lines io.Reader) []byte { func ReaderToBytes(lines io.Reader) []byte {

View file

@ -98,7 +98,7 @@ func TestRenderThing(t *testing.T) {
}{ }{
{PAGE_SIMPLE_TITLE, TEMPLATE_TITLE, "simple template"}, {PAGE_SIMPLE_TITLE, TEMPLATE_TITLE, "simple template"},
{PAGE_SIMPLE_TITLE, TEMPLATE_FUNC, "simple-template"}, {PAGE_SIMPLE_TITLE, TEMPLATE_FUNC, "simple-template"},
{PAGE_WITH_MD, TEMPLATE_CONTENT, "<h1>heading 1</h1>\n\n<p>text</p>\n\n<h2>heading 2</h2>\n\n<p>more text</p>\n"}, {PAGE_WITH_MD, TEMPLATE_CONTENT, "\n\n<h1 id=\"toc_0\">heading 1</h1>\n\n<p>text</p>\n\n<h2 id=\"toc_1\">heading 2</h2>\n\n<p>more text</p>\n"},
{SIMPLE_PAGE_RFC3339_DATE, TEMPLATE_DATE, "2013-05-17 16:59:30 &#43;0000 UTC"}, {SIMPLE_PAGE_RFC3339_DATE, TEMPLATE_DATE, "2013-05-17 16:59:30 &#43;0000 UTC"},
} }
@ -265,14 +265,14 @@ func TestSkipRender(t *testing.T) {
doc string doc string
expected string expected string
}{ }{
{"sect/doc1.html", "<h1>title</h1>\n\n<p>some <em>content</em></p>\n"}, {"sect/doc1.html", "\n\n<h1 id=\"toc_0\">title</h1>\n\n<p>some <em>content</em></p>\n"},
{"sect/doc2.html", "<!doctype html><html><body>more content</body></html>"}, {"sect/doc2.html", "<!doctype html><html><body>more content</body></html>"},
{"sect/doc3.html", "<h1>doc3</h1>\n\n<p><em>some</em> content</p>\n"}, {"sect/doc3.html", "\n\n<h1 id=\"toc_0\">doc3</h1>\n\n<p><em>some</em> content</p>\n"},
{"sect/doc4.html", "<h1>doc4</h1>\n\n<p><em>some content</em></p>\n"}, {"sect/doc4.html", "\n\n<h1 id=\"toc_0\">doc4</h1>\n\n<p><em>some content</em></p>\n"},
{"sect/doc5.html", "<!doctype html><html><head><script src=\"script.js\"></script></head><body>body5</body></html>"}, {"sect/doc5.html", "<!doctype html><html><head><script src=\"script.js\"></script></head><body>body5</body></html>"},
{"sect/doc6.html", "<!doctype html><html><head><script src=\"http://auth/bub/script.js\"></script></head><body>body5</body></html>"}, {"sect/doc6.html", "<!doctype html><html><head><script src=\"http://auth/bub/script.js\"></script></head><body>body5</body></html>"},
{"doc7.html", "<html><body>doc7 content</body></html>"}, {"doc7.html", "<html><body>doc7 content</body></html>"},
{"sect/doc8.html", "<h1>title</h1>\n\n<p>some <em>content</em></p>\n"}, {"sect/doc8.html", "\n\n<h1 id=\"toc_0\">title</h1>\n\n<p>some <em>content</em></p>\n"},
} }
for _, test := range tests { for _, test := range tests {