gofmt all go code

This commit is contained in:
Tibor Vass 2014-01-29 14:50:31 -08:00 committed by spf13
parent ff9f6e1b2a
commit 6dd2e9a49a
14 changed files with 2013 additions and 2013 deletions

View File

@ -52,11 +52,11 @@ func server(cmd *cobra.Command, args []string) {
BaseUrl = "http://" + BaseUrl BaseUrl = "http://" + BaseUrl
} }
if serverAppend { if serverAppend {
Config.BaseUrl = strings.TrimSuffix(BaseUrl, "/") + ":" + strconv.Itoa(serverPort) Config.BaseUrl = strings.TrimSuffix(BaseUrl, "/") + ":" + strconv.Itoa(serverPort)
} else { } else {
Config.BaseUrl = strings.TrimSuffix(BaseUrl, "/") Config.BaseUrl = strings.TrimSuffix(BaseUrl, "/")
} }
build(serverWatch) build(serverWatch)

View File

@ -14,32 +14,32 @@
package helpers package helpers
import ( import (
"bytes" "bytes"
"log" "log"
"os/exec" "os/exec"
"strings" "strings"
) )
func Highlight(code string, lexer string) string { func Highlight(code string, lexer string) string {
var pygmentsBin = "pygmentize" var pygmentsBin = "pygmentize"
if _, err := exec.LookPath(pygmentsBin); err != nil { if _, err := exec.LookPath(pygmentsBin); err != nil {
log.Print("Highlighting requries Pygments to be installed and in the path") log.Print("Highlighting requries Pygments to be installed and in the path")
return code return code
} }
var out bytes.Buffer var out bytes.Buffer
var stderr bytes.Buffer var stderr bytes.Buffer
cmd := exec.Command(pygmentsBin, "-l"+lexer, "-fhtml", "-O style=monokai,noclasses=true,encoding=utf-8") cmd := exec.Command(pygmentsBin, "-l"+lexer, "-fhtml", "-O style=monokai,noclasses=true,encoding=utf-8")
cmd.Stdin = strings.NewReader(code) cmd.Stdin = strings.NewReader(code)
cmd.Stdout = &out cmd.Stdout = &out
cmd.Stderr = &stderr cmd.Stderr = &stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
log.Print(stderr.String()) log.Print(stderr.String())
return code return code
} }
return out.String() return out.String()
} }

View File

@ -36,7 +36,7 @@ type Config struct {
Params map[string]interface{} Params map[string]interface{}
Permalinks PermalinkOverrides Permalinks PermalinkOverrides
BuildDrafts, UglyUrls, Verbose bool BuildDrafts, UglyUrls, Verbose bool
CanonifyUrls bool CanonifyUrls bool
} }
var c Config var c Config

View File

@ -1,18 +1,18 @@
package hugolib package hugolib
import ( import (
"strings" "strings"
"testing" "testing"
) )
func TestSitePossibleIndexes(t *testing.T) { func TestSitePossibleIndexes(t *testing.T) {
site := new(Site) site := new(Site)
page, _ := ReadFrom(strings.NewReader(PAGE_YAML_WITH_INDEXES_A), "path/to/page") page, _ := ReadFrom(strings.NewReader(PAGE_YAML_WITH_INDEXES_A), "path/to/page")
site.Pages = append(site.Pages, page) site.Pages = append(site.Pages, page)
indexes := site.possibleIndexes() indexes := site.possibleIndexes()
if !compareStringSlice(indexes, []string{"tags", "categories"}) { if !compareStringSlice(indexes, []string{"tags", "categories"}) {
if !compareStringSlice(indexes, []string{"categories", "tags"}) { if !compareStringSlice(indexes, []string{"categories", "tags"}) {
t.Fatalf("possible indexes do not match [tags categories]. Got: %s", indexes) t.Fatalf("possible indexes do not match [tags categories]. Got: %s", indexes)
} }
} }
} }

View File

@ -14,29 +14,29 @@
package hugolib package hugolib
import ( import (
"html/template" "html/template"
"time" "time"
) )
type Node struct { type Node struct {
RSSLink template.HTML RSSLink template.HTML
Site SiteInfo Site SiteInfo
// layout string // layout string
Data map[string]interface{} Data map[string]interface{}
Title string Title string
Description string Description string
Keywords []string Keywords []string
Date time.Time Date time.Time
UrlPath UrlPath
} }
func (n Node) RSSlink() template.HTML { func (n Node) RSSlink() template.HTML {
return n.RSSLink return n.RSSLink
} }
type UrlPath struct { type UrlPath struct {
Url string Url string
Permalink template.HTML Permalink template.HTML
Slug string Slug string
Section string Section string
} }

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@
package hugolib package hugolib
import ( import (
"sort" "sort"
) )
/* /*
@ -23,27 +23,27 @@ import (
// A type to implement the sort interface for Pages // A type to implement the sort interface for Pages
type PageSorter struct { type PageSorter struct {
pages Pages pages Pages
by PageBy by PageBy
} }
// Closure used in the Sort.Less method. // Closure used in the Sort.Less method.
type PageBy func(p1, p2 *Page) bool type PageBy func(p1, p2 *Page) bool
func (by PageBy) Sort(pages Pages) { func (by PageBy) Sort(pages Pages) {
ps := &PageSorter{ ps := &PageSorter{
pages: pages, pages: pages,
by: by, // The Sort method's receiver is the function (closure) that defines the sort order. by: by, // The Sort method's receiver is the function (closure) that defines the sort order.
} }
sort.Sort(ps) sort.Sort(ps)
} }
var DefaultPageSort = func(p1, p2 *Page) bool { var DefaultPageSort = func(p1, p2 *Page) bool {
if p1.Weight == p2.Weight { if p1.Weight == p2.Weight {
return p1.Date.Unix() > p2.Date.Unix() return p1.Date.Unix() > p2.Date.Unix()
} else { } else {
return p1.Weight < p2.Weight return p1.Weight < p2.Weight
} }
} }
func (ps *PageSorter) Len() int { return len(ps.pages) } func (ps *PageSorter) Len() int { return len(ps.pages) }
@ -53,44 +53,44 @@ func (ps *PageSorter) Swap(i, j int) { ps.pages[i], ps.pages[j] = ps.pages[j], p
func (ps *PageSorter) Less(i, j int) bool { return ps.by(ps.pages[i], ps.pages[j]) } func (ps *PageSorter) Less(i, j int) bool { return ps.by(ps.pages[i], ps.pages[j]) }
func (p Pages) Sort() { func (p Pages) Sort() {
PageBy(DefaultPageSort).Sort(p) PageBy(DefaultPageSort).Sort(p)
} }
func (p Pages) Limit(n int) Pages { func (p Pages) Limit(n int) Pages {
if len(p) < n { if len(p) < n {
return p[0:n] return p[0:n]
} else { } else {
return p return p
} }
} }
func (p Pages) ByWeight() Pages { func (p Pages) ByWeight() Pages {
PageBy(DefaultPageSort).Sort(p) PageBy(DefaultPageSort).Sort(p)
return p return p
} }
func (p Pages) ByDate() Pages { func (p Pages) ByDate() Pages {
date := func(p1, p2 *Page) bool { date := func(p1, p2 *Page) bool {
return p1.Date.Unix() < p2.Date.Unix() return p1.Date.Unix() < p2.Date.Unix()
} }
PageBy(date).Sort(p) PageBy(date).Sort(p)
return p return p
} }
func (p Pages) ByLength() Pages { func (p Pages) ByLength() Pages {
length := func(p1, p2 *Page) bool { length := func(p1, p2 *Page) bool {
return len(p1.Content) < len(p2.Content) return len(p1.Content) < len(p2.Content)
} }
PageBy(length).Sort(p) PageBy(length).Sort(p)
return p return p
} }
func (p Pages) Reverse() Pages { func (p Pages) Reverse() Pages {
for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 { for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 {
p[i], p[j] = p[j], p[i] p[i], p[j] = p[j], p[i]
} }
return p return p
} }

View File

@ -1,34 +1,34 @@
package hugolib package hugolib
import ( import (
"html/template" "html/template"
"path" "path"
"strings" "strings"
"testing" "testing"
"time" "time"
) )
var EMPTY_PAGE = "" var EMPTY_PAGE = ""
const ( const (
SIMPLE_PAGE = "---\ntitle: Simple\n---\nSimple Page\n" SIMPLE_PAGE = "---\ntitle: Simple\n---\nSimple Page\n"
INVALID_FRONT_MATTER_MISSING = "This is a test" INVALID_FRONT_MATTER_MISSING = "This is a test"
RENDER_NO_FRONT_MATTER = "<!doctype><html><head></head><body>This is a test</body></html>" RENDER_NO_FRONT_MATTER = "<!doctype><html><head></head><body>This is a test</body></html>"
INVALID_FRONT_MATTER_SHORT_DELIM = ` INVALID_FRONT_MATTER_SHORT_DELIM = `
-- --
title: Short delim start title: Short delim start
--- ---
Short Delim Short Delim
` `
INVALID_FRONT_MATTER_SHORT_DELIM_ENDING = ` INVALID_FRONT_MATTER_SHORT_DELIM_ENDING = `
--- ---
title: Short delim ending title: Short delim ending
-- --
Short Delim Short Delim
` `
INVALID_FRONT_MATTER_LEADING_WS = ` INVALID_FRONT_MATTER_LEADING_WS = `
--- ---
title: Leading WS title: Leading WS
@ -36,7 +36,7 @@ title: Leading WS
Leading Leading
` `
SIMPLE_PAGE_JSON = ` SIMPLE_PAGE_JSON = `
{ {
"title": "spf13-vim 3.0 release and new website", "title": "spf13-vim 3.0 release and new website",
"description": "spf13-vim is a cross platform distribution of vim plugins and resources for Vim.", "description": "spf13-vim is a cross platform distribution of vim plugins and resources for Vim.",
@ -51,7 +51,7 @@ Leading
Content of the file goes Here Content of the file goes Here
` `
SIMPLE_PAGE_JSON_LOOSE = ` SIMPLE_PAGE_JSON_LOOSE = `
{ {
"title": "spf13-vim 3.0 release and new website" "title": "spf13-vim 3.0 release and new website"
"description": "spf13-vim is a cross platform distribution of vim plugins and resources for Vim." "description": "spf13-vim is a cross platform distribution of vim plugins and resources for Vim."
@ -66,8 +66,8 @@ Content of the file goes Here
Content of the file goes Here Content of the file goes Here
` `
SIMPLE_PAGE_RFC3339_DATE = "---\ntitle: RFC3339 Date\ndate: \"2013-05-17T16:59:30Z\"\n---\nrfc3339 content" SIMPLE_PAGE_RFC3339_DATE = "---\ntitle: RFC3339 Date\ndate: \"2013-05-17T16:59:30Z\"\n---\nrfc3339 content"
SIMPLE_PAGE_JSON_MULTIPLE = ` SIMPLE_PAGE_JSON_MULTIPLE = `
{ {
"title": "foobar", "title": "foobar",
"customData": { "foo": "bar" }, "customData": { "foo": "bar" },
@ -76,34 +76,34 @@ Content of the file goes Here
Some text Some text
` `
SIMPLE_PAGE_JSON_COMPACT = ` SIMPLE_PAGE_JSON_COMPACT = `
{"title":"foobar","customData":{"foo":"bar"},"date":"2012-08-06"} {"title":"foobar","customData":{"foo":"bar"},"date":"2012-08-06"}
Text Text
` `
SIMPLE_PAGE_NOLAYOUT = `--- SIMPLE_PAGE_NOLAYOUT = `---
title: simple_no_layout title: simple_no_layout
--- ---
No Layout called out` No Layout called out`
SIMPLE_PAGE_LAYOUT_FOOBAR = `--- SIMPLE_PAGE_LAYOUT_FOOBAR = `---
title: simple layout foobar title: simple layout foobar
layout: foobar layout: foobar
--- ---
Layout foobar` Layout foobar`
SIMPLE_PAGE_TYPE_FOOBAR = `--- SIMPLE_PAGE_TYPE_FOOBAR = `---
type: foobar type: foobar
--- ---
type foobar` type foobar`
SIMPLE_PAGE_TYPE_LAYOUT = `--- SIMPLE_PAGE_TYPE_LAYOUT = `---
type: barfoo type: barfoo
layout: buzfoo layout: buzfoo
--- ---
type and layout set` type and layout set`
SIMPLE_PAGE_WITH_SUMMARY_DELIMITER = `--- SIMPLE_PAGE_WITH_SUMMARY_DELIMITER = `---
title: Simple title: Simple
--- ---
Summary Next Line Summary Next Line
@ -111,7 +111,7 @@ Summary Next Line
<!--more--> <!--more-->
Some more text Some more text
` `
SIMPLE_PAGE_WITH_SHORTCODE_IN_SUMMARY = `--- SIMPLE_PAGE_WITH_SHORTCODE_IN_SUMMARY = `---
title: Simple title: Simple
--- ---
Summary Next Line. {{% img src="/not/real" %}}. Summary Next Line. {{% img src="/not/real" %}}.
@ -120,7 +120,7 @@ More text here.
Some more text Some more text
` `
SIMPLE_PAGE_WITH_SUMMARY_DELIMITER_SAME_LINE = `--- SIMPLE_PAGE_WITH_SUMMARY_DELIMITER_SAME_LINE = `---
title: Simple title: Simple
--- ---
Summary Same Line<!--more--> Summary Same Line<!--more-->
@ -128,7 +128,7 @@ Summary Same Line<!--more-->
Some more text Some more text
` `
SIMPLE_PAGE_WITH_LONG_CONTENT = `--- SIMPLE_PAGE_WITH_LONG_CONTENT = `---
title: Simple title: Simple
--- ---
@ -174,7 +174,7 @@ in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum.` officia deserunt mollit anim id est laborum.`
PAGE_WITH_TOC = `--- PAGE_WITH_TOC = `---
title: TOC title: TOC
--- ---
For some moments the old man did not reply. He stood with bowed head, buried in deep thought. But at last he spoke. For some moments the old man did not reply. He stood with bowed head, buried in deep thought. But at last he spoke.
@ -215,330 +215,330 @@ a_date = 1979-05-27T07:32:00Z
Front Matter with various frontmatter types` Front Matter with various frontmatter types`
func checkError(t *testing.T, err error, expected string) { func checkError(t *testing.T, err error, expected string) {
if err == nil { if err == nil {
t.Fatalf("err is nil. Expected: %s", expected) t.Fatalf("err is nil. Expected: %s", expected)
} }
if err.Error() != expected { if err.Error() != expected {
t.Errorf("err.Error() returned: '%s'. Expected: '%s'", err.Error(), expected) t.Errorf("err.Error() returned: '%s'. Expected: '%s'", err.Error(), expected)
} }
} }
func TestDegenerateEmptyPageZeroLengthName(t *testing.T) { func TestDegenerateEmptyPageZeroLengthName(t *testing.T) {
_, err := ReadFrom(strings.NewReader(EMPTY_PAGE), "") _, err := ReadFrom(strings.NewReader(EMPTY_PAGE), "")
if err == nil { if err == nil {
t.Fatalf("A zero length page name must return an error") t.Fatalf("A zero length page name must return an error")
} }
checkError(t, err, "Zero length page name") checkError(t, err, "Zero length page name")
} }
func TestDegenerateEmptyPage(t *testing.T) { func TestDegenerateEmptyPage(t *testing.T) {
_, err := ReadFrom(strings.NewReader(EMPTY_PAGE), "test") _, err := ReadFrom(strings.NewReader(EMPTY_PAGE), "test")
if err != nil { if err != nil {
t.Fatalf("Empty files should not trigger an error. Should be able to touch a file while watching without erroring out.") t.Fatalf("Empty files should not trigger an error. Should be able to touch a file while watching without erroring out.")
} }
//checkError(t, err, "EOF") //checkError(t, err, "EOF")
} }
func checkPageTitle(t *testing.T, page *Page, title string) { func checkPageTitle(t *testing.T, page *Page, title string) {
if page.Title != title { if page.Title != title {
t.Fatalf("Page title is: %s. Expected %s", page.Title, title) t.Fatalf("Page title is: %s. Expected %s", page.Title, title)
} }
} }
func checkPageContent(t *testing.T, page *Page, content string) { func checkPageContent(t *testing.T, page *Page, content string) {
if page.Content != template.HTML(content) { if page.Content != template.HTML(content) {
t.Fatalf("Page content is: %q\nExpected: %q", page.Content, content) t.Fatalf("Page content is: %q\nExpected: %q", page.Content, content)
} }
} }
func checkPageTOC(t *testing.T, page *Page, toc string) { func checkPageTOC(t *testing.T, page *Page, toc string) {
if page.TableOfContents != template.HTML(toc) { if page.TableOfContents != template.HTML(toc) {
t.Fatalf("Page TableOfContents is: %q.\nExpected %q", page.TableOfContents, toc) t.Fatalf("Page TableOfContents is: %q.\nExpected %q", page.TableOfContents, toc)
} }
} }
func checkPageSummary(t *testing.T, page *Page, summary string) { func checkPageSummary(t *testing.T, page *Page, summary string) {
if page.Summary != template.HTML(summary) { if page.Summary != template.HTML(summary) {
t.Fatalf("Page summary is: %q.\nExpected %q", page.Summary, summary) t.Fatalf("Page summary is: %q.\nExpected %q", page.Summary, summary)
} }
} }
func checkPageType(t *testing.T, page *Page, pageType string) { func checkPageType(t *testing.T, page *Page, pageType string) {
if page.Type() != pageType { if page.Type() != pageType {
t.Fatalf("Page type is: %s. Expected: %s", page.Type(), pageType) t.Fatalf("Page type is: %s. Expected: %s", page.Type(), pageType)
} }
} }
func checkPageLayout(t *testing.T, page *Page, layout ...string) { func checkPageLayout(t *testing.T, page *Page, layout ...string) {
if !listEqual(page.Layout(), layout) { if !listEqual(page.Layout(), layout) {
t.Fatalf("Page layout is: %s. Expected: %s", page.Layout(), layout) t.Fatalf("Page layout is: %s. Expected: %s", page.Layout(), layout)
} }
} }
func checkPageDate(t *testing.T, page *Page, time time.Time) { func checkPageDate(t *testing.T, page *Page, time time.Time) {
if page.Date != time { if page.Date != time {
t.Fatalf("Page date is: %s. Expected: %s", page.Date, time) t.Fatalf("Page date is: %s. Expected: %s", page.Date, time)
} }
} }
func checkTruncation(t *testing.T, page *Page, shouldBe bool, msg string) { func checkTruncation(t *testing.T, page *Page, shouldBe bool, msg string) {
if page.Summary == "" { if page.Summary == "" {
t.Fatal("page has no summary, can not check truncation") t.Fatal("page has no summary, can not check truncation")
} }
if page.Truncated != shouldBe { if page.Truncated != shouldBe {
if shouldBe { if shouldBe {
t.Fatalf("page wasn't truncated: %s", msg) t.Fatalf("page wasn't truncated: %s", msg)
} else { } else {
t.Fatalf("page was truncated: %s", msg) t.Fatalf("page was truncated: %s", msg)
} }
} }
} }
func TestCreateNewPage(t *testing.T) { func TestCreateNewPage(t *testing.T) {
p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE), "simple.md") p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE), "simple.md")
p.Convert() p.Convert()
if err != nil { if err != nil {
t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)
} }
checkPageTitle(t, p, "Simple") checkPageTitle(t, p, "Simple")
checkPageContent(t, p, "<p>Simple Page</p>\n") checkPageContent(t, p, "<p>Simple Page</p>\n")
checkPageSummary(t, p, "Simple Page") checkPageSummary(t, p, "Simple Page")
checkPageType(t, p, "page") checkPageType(t, p, "page")
checkPageLayout(t, p, "page/single.html", "single.html") checkPageLayout(t, p, "page/single.html", "single.html")
checkTruncation(t, p, false, "simple short page") checkTruncation(t, p, false, "simple short page")
} }
func TestPageWithDelimiter(t *testing.T) { func TestPageWithDelimiter(t *testing.T) {
p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SUMMARY_DELIMITER), "simple.md") p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SUMMARY_DELIMITER), "simple.md")
p.Convert() p.Convert()
if err != nil { if err != nil {
t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)
} }
checkPageTitle(t, p, "Simple") checkPageTitle(t, p, "Simple")
checkPageContent(t, p, "<p>Summary Next Line</p>\n\n<p>Some more text</p>\n") checkPageContent(t, p, "<p>Summary Next Line</p>\n\n<p>Some more text</p>\n")
checkPageSummary(t, p, "<p>Summary Next Line</p>\n") checkPageSummary(t, p, "<p>Summary Next Line</p>\n")
checkPageType(t, p, "page") checkPageType(t, p, "page")
checkPageLayout(t, p, "page/single.html", "single.html") checkPageLayout(t, p, "page/single.html", "single.html")
checkTruncation(t, p, true, "page with summary delimiter") checkTruncation(t, p, true, "page with summary delimiter")
} }
func TestPageWithShortCodeInSummary(t *testing.T) { func TestPageWithShortCodeInSummary(t *testing.T) {
p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SHORTCODE_IN_SUMMARY), "simple.md") p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SHORTCODE_IN_SUMMARY), "simple.md")
p.Convert() p.Convert()
if err != nil { if err != nil {
t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)
} }
checkPageTitle(t, p, "Simple") checkPageTitle(t, p, "Simple")
checkPageContent(t, p, "<p>Summary Next Line. {{% img src=&ldquo;/not/real&rdquo; %}}.\nMore text here.</p>\n\n<p>Some more text</p>\n") checkPageContent(t, p, "<p>Summary Next Line. {{% img src=&ldquo;/not/real&rdquo; %}}.\nMore text here.</p>\n\n<p>Some more text</p>\n")
checkPageSummary(t, p, "Summary Next Line. . More text here. Some more text") checkPageSummary(t, p, "Summary Next Line. . More text here. Some more text")
checkPageType(t, p, "page") checkPageType(t, p, "page")
checkPageLayout(t, p, "page/single.html", "single.html") checkPageLayout(t, p, "page/single.html", "single.html")
} }
func TestTableOfContents(t *testing.T) { func TestTableOfContents(t *testing.T) {
p, err := ReadFrom(strings.NewReader(PAGE_WITH_TOC), "tocpage.md") p, err := ReadFrom(strings.NewReader(PAGE_WITH_TOC), "tocpage.md")
p.Convert() p.Convert()
if err != nil { if err != nil {
t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)
} }
checkPageContent(t, p, "\n\n<p>For some moments the old man did not reply. He stood with bowed head, buried in deep thought. But at last he spoke.</p>\n\n<h2 id=\"toc_0\">AA</h2>\n\n<p>I have no idea, of course, how long it took me to reach the limit of the plain,\nbut at last I entered the foothills, following a pretty little canyon upward\ntoward the mountains. Beside me frolicked a laughing brooklet, hurrying upon\nits noisy way down to the silent sea. In its quieter pools I discovered many\nsmall fish, of four-or five-pound weight I should imagine. In appearance,\nexcept as to size and color, they were not unlike the whale of our own seas. As\nI watched them playing about I discovered, not only that they suckled their\nyoung, but that at intervals they rose to the surface to breathe as well as to\nfeed upon certain grasses and a strange, scarlet lichen which grew upon the\nrocks just above the water line.</p>\n\n<h3 id=\"toc_1\">AAA</h3>\n\n<p>I remember I felt an extraordinary persuasion that I was being played with,\nthat presently, when I was upon the very verge of safety, this mysterious\ndeath&mdash;as swift as the passage of light&mdash;would leap after me from the pit about\nthe cylinder and strike me down. ## BB</p>\n\n<h3 id=\"toc_2\">BBB</h3>\n\n<p>&ldquo;You&rsquo;re a great Granser,&rdquo; he cried delightedly, &ldquo;always making believe them little marks mean something.&rdquo;</p>\n") checkPageContent(t, p, "\n\n<p>For some moments the old man did not reply. He stood with bowed head, buried in deep thought. But at last he spoke.</p>\n\n<h2 id=\"toc_0\">AA</h2>\n\n<p>I have no idea, of course, how long it took me to reach the limit of the plain,\nbut at last I entered the foothills, following a pretty little canyon upward\ntoward the mountains. Beside me frolicked a laughing brooklet, hurrying upon\nits noisy way down to the silent sea. In its quieter pools I discovered many\nsmall fish, of four-or five-pound weight I should imagine. In appearance,\nexcept as to size and color, they were not unlike the whale of our own seas. As\nI watched them playing about I discovered, not only that they suckled their\nyoung, but that at intervals they rose to the surface to breathe as well as to\nfeed upon certain grasses and a strange, scarlet lichen which grew upon the\nrocks just above the water line.</p>\n\n<h3 id=\"toc_1\">AAA</h3>\n\n<p>I remember I felt an extraordinary persuasion that I was being played with,\nthat presently, when I was upon the very verge of safety, this mysterious\ndeath&mdash;as swift as the passage of light&mdash;would leap after me from the pit about\nthe cylinder and strike me down. ## BB</p>\n\n<h3 id=\"toc_2\">BBB</h3>\n\n<p>&ldquo;You&rsquo;re a great Granser,&rdquo; he cried delightedly, &ldquo;always making believe them little marks mean something.&rdquo;</p>\n")
checkPageTOC(t, p, "<nav id=\"TableOfContents\">\n<ul>\n<li>\n<ul>\n<li><a href=\"#toc_0\">AA</a>\n<ul>\n<li><a href=\"#toc_1\">AAA</a></li>\n<li><a href=\"#toc_2\">BBB</a></li>\n</ul></li>\n</ul></li>\n</ul>\n</nav>") checkPageTOC(t, p, "<nav id=\"TableOfContents\">\n<ul>\n<li>\n<ul>\n<li><a href=\"#toc_0\">AA</a>\n<ul>\n<li><a href=\"#toc_1\">AAA</a></li>\n<li><a href=\"#toc_2\">BBB</a></li>\n</ul></li>\n</ul></li>\n</ul>\n</nav>")
} }
func TestPageWithMoreTag(t *testing.T) { func TestPageWithMoreTag(t *testing.T) {
p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SUMMARY_DELIMITER_SAME_LINE), "simple.md") p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SUMMARY_DELIMITER_SAME_LINE), "simple.md")
p.Convert() p.Convert()
if err != nil { if err != nil {
t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)
} }
checkPageTitle(t, p, "Simple") checkPageTitle(t, p, "Simple")
checkPageContent(t, p, "<p>Summary Same Line</p>\n\n<p>Some more text</p>\n") checkPageContent(t, p, "<p>Summary Same Line</p>\n\n<p>Some more text</p>\n")
checkPageSummary(t, p, "<p>Summary Same Line</p>\n") checkPageSummary(t, p, "<p>Summary Same Line</p>\n")
checkPageType(t, p, "page") checkPageType(t, p, "page")
checkPageLayout(t, p, "page/single.html", "single.html") checkPageLayout(t, p, "page/single.html", "single.html")
} }
func TestPageWithDate(t *testing.T) { func TestPageWithDate(t *testing.T) {
p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_RFC3339_DATE), "simple") p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_RFC3339_DATE), "simple")
p.Convert() p.Convert()
if err != nil { if err != nil {
t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)
} }
d, err := time.Parse(time.RFC3339, "2013-05-17T16:59:30Z") d, err := time.Parse(time.RFC3339, "2013-05-17T16:59:30Z")
if err != nil { if err != nil {
t.Fatalf("Unable to prase page.") t.Fatalf("Unable to prase page.")
} }
checkPageDate(t, p, d) checkPageDate(t, p, d)
} }
func TestWordCount(t *testing.T) { func TestWordCount(t *testing.T) {
p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_LONG_CONTENT), "simple.md") p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_LONG_CONTENT), "simple.md")
p.Convert() p.Convert()
p.analyzePage() p.analyzePage()
if err != nil { if err != nil {
t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)
} }
if p.WordCount != 483 { if p.WordCount != 483 {
t.Fatalf("incorrect word count. expected %v, got %v", 483, p.WordCount) t.Fatalf("incorrect word count. expected %v, got %v", 483, p.WordCount)
} }
if p.FuzzyWordCount != 500 { if p.FuzzyWordCount != 500 {
t.Fatalf("incorrect word count. expected %v, got %v", 500, p.WordCount) t.Fatalf("incorrect word count. expected %v, got %v", 500, p.WordCount)
} }
if p.ReadingTime != 3 { if p.ReadingTime != 3 {
t.Fatalf("incorrect min read. expected %v, got %v", 3, p.ReadingTime) t.Fatalf("incorrect min read. expected %v, got %v", 3, p.ReadingTime)
} }
checkTruncation(t, p, true, "long page") checkTruncation(t, p, true, "long page")
} }
func TestCreatePage(t *testing.T) { func TestCreatePage(t *testing.T) {
var tests = []struct { var tests = []struct {
r string r string
}{ }{
{SIMPLE_PAGE_JSON}, {SIMPLE_PAGE_JSON},
{SIMPLE_PAGE_JSON_LOOSE}, {SIMPLE_PAGE_JSON_LOOSE},
{SIMPLE_PAGE_JSON_MULTIPLE}, {SIMPLE_PAGE_JSON_MULTIPLE},
//{strings.NewReader(SIMPLE_PAGE_JSON_COMPACT)}, //{strings.NewReader(SIMPLE_PAGE_JSON_COMPACT)},
} }
for _, test := range tests { for _, test := range tests {
if _, err := ReadFrom(strings.NewReader(test.r), "page"); err != nil { if _, err := ReadFrom(strings.NewReader(test.r), "page"); err != nil {
t.Errorf("Unable to parse page: %s", err) t.Errorf("Unable to parse page: %s", err)
} }
} }
} }
func TestDegenerateInvalidFrontMatterShortDelim(t *testing.T) { func TestDegenerateInvalidFrontMatterShortDelim(t *testing.T) {
var tests = []struct { var tests = []struct {
r string r string
err string err string
}{ }{
{INVALID_FRONT_MATTER_SHORT_DELIM_ENDING, "Unable to read frontmatter at filepos 45: EOF"}, {INVALID_FRONT_MATTER_SHORT_DELIM_ENDING, "Unable to read frontmatter at filepos 45: EOF"},
} }
for _, test := range tests { for _, test := range tests {
_, err := ReadFrom(strings.NewReader(test.r), "invalid/front/matter/short/delim") _, err := ReadFrom(strings.NewReader(test.r), "invalid/front/matter/short/delim")
checkError(t, err, test.err) checkError(t, err, test.err)
} }
} }
func TestShouldRenderContent(t *testing.T) { func TestShouldRenderContent(t *testing.T) {
var tests = []struct { var tests = []struct {
text string text string
render bool render bool
}{ }{
{INVALID_FRONT_MATTER_MISSING, true}, {INVALID_FRONT_MATTER_MISSING, true},
// TODO how to deal with malformed frontmatter. In this case it'll be rendered as markdown. // TODO how to deal with malformed frontmatter. In this case it'll be rendered as markdown.
{INVALID_FRONT_MATTER_SHORT_DELIM, true}, {INVALID_FRONT_MATTER_SHORT_DELIM, true},
{RENDER_NO_FRONT_MATTER, false}, {RENDER_NO_FRONT_MATTER, false},
} }
for _, test := range tests { for _, test := range tests {
p := pageMust(ReadFrom(strings.NewReader(test.text), "render/front/matter")) p := pageMust(ReadFrom(strings.NewReader(test.text), "render/front/matter"))
if p.IsRenderable() != test.render { if p.IsRenderable() != test.render {
t.Errorf("expected p.IsRenderable() == %t, got %t", test.render, p.IsRenderable()) t.Errorf("expected p.IsRenderable() == %t, got %t", test.render, p.IsRenderable())
} }
} }
} }
func TestDifferentFrontMatterVarTypes(t *testing.T) { func TestDifferentFrontMatterVarTypes(t *testing.T) {
page, _ := ReadFrom(strings.NewReader(PAGE_WITH_VARIOUS_FRONTMATTER_TYPES), "test/file1.md") page, _ := ReadFrom(strings.NewReader(PAGE_WITH_VARIOUS_FRONTMATTER_TYPES), "test/file1.md")
dateval, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") dateval, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
if page.GetParam("a_string") != "bar" { if page.GetParam("a_string") != "bar" {
t.Errorf("frontmatter not handling strings correctly should be %s, got: %s", "bar", page.GetParam("a_string")) t.Errorf("frontmatter not handling strings correctly should be %s, got: %s", "bar", page.GetParam("a_string"))
} }
if page.GetParam("an_integer") != 1 { if page.GetParam("an_integer") != 1 {
t.Errorf("frontmatter not handling ints correctly should be %s, got: %s", "1", page.GetParam("an_integer")) t.Errorf("frontmatter not handling ints correctly should be %s, got: %s", "1", page.GetParam("an_integer"))
} }
if page.GetParam("a_float") != 1.3 { if page.GetParam("a_float") != 1.3 {
t.Errorf("frontmatter not handling floats correctly should be %s, got: %s", 1.3, page.GetParam("a_float")) t.Errorf("frontmatter not handling floats correctly should be %s, got: %s", 1.3, page.GetParam("a_float"))
} }
if page.GetParam("a_bool") != false { if page.GetParam("a_bool") != false {
t.Errorf("frontmatter not handling bools correctly should be %s, got: %s", false, page.GetParam("a_bool")) t.Errorf("frontmatter not handling bools correctly should be %s, got: %s", false, page.GetParam("a_bool"))
} }
if page.GetParam("a_date") != dateval { if page.GetParam("a_date") != dateval {
t.Errorf("frontmatter not handling dates correctly should be %s, got: %s", dateval, page.GetParam("a_date")) t.Errorf("frontmatter not handling dates correctly should be %s, got: %s", dateval, page.GetParam("a_date"))
} }
} }
func TestDegenerateInvalidFrontMatterLeadingWhitespace(t *testing.T) { func TestDegenerateInvalidFrontMatterLeadingWhitespace(t *testing.T) {
_, err := ReadFrom(strings.NewReader(INVALID_FRONT_MATTER_LEADING_WS), "invalid/front/matter/leading/ws") _, err := ReadFrom(strings.NewReader(INVALID_FRONT_MATTER_LEADING_WS), "invalid/front/matter/leading/ws")
if err != nil { if err != nil {
t.Fatalf("Unable to parse front matter given leading whitespace: %s", err) t.Fatalf("Unable to parse front matter given leading whitespace: %s", err)
} }
} }
func TestSectionEvaluation(t *testing.T) { func TestSectionEvaluation(t *testing.T) {
page, _ := ReadFrom(strings.NewReader(SIMPLE_PAGE), "blue/file1.md") page, _ := ReadFrom(strings.NewReader(SIMPLE_PAGE), "blue/file1.md")
if page.Section != "blue" { if page.Section != "blue" {
t.Errorf("Section should be %s, got: %s", "blue", page.Section) t.Errorf("Section should be %s, got: %s", "blue", page.Section)
} }
} }
func L(s ...string) []string { func L(s ...string) []string {
return s return s
} }
func TestLayoutOverride(t *testing.T) { func TestLayoutOverride(t *testing.T) {
var ( var (
path_content_two_dir = path.Join("content", "dub", "sub", "file1.md") path_content_two_dir = path.Join("content", "dub", "sub", "file1.md")
path_content_one_dir = path.Join("content", "gub", "file1.md") path_content_one_dir = path.Join("content", "gub", "file1.md")
path_content_no_dir = path.Join("content", "file1") path_content_no_dir = path.Join("content", "file1")
path_one_directory = path.Join("fub", "file1.md") path_one_directory = path.Join("fub", "file1.md")
path_no_directory = path.Join("file1.md") path_no_directory = path.Join("file1.md")
) )
tests := []struct { tests := []struct {
content string content string
path string path string
expectedLayout []string expectedLayout []string
}{ }{
{SIMPLE_PAGE_NOLAYOUT, path_content_two_dir, L("dub/sub/single.html", "dub/single.html", "single.html")}, {SIMPLE_PAGE_NOLAYOUT, path_content_two_dir, L("dub/sub/single.html", "dub/single.html", "single.html")},
{SIMPLE_PAGE_NOLAYOUT, path_content_one_dir, L("gub/single.html", "single.html")}, {SIMPLE_PAGE_NOLAYOUT, path_content_one_dir, L("gub/single.html", "single.html")},
{SIMPLE_PAGE_NOLAYOUT, path_content_no_dir, L("page/single.html", "single.html")}, {SIMPLE_PAGE_NOLAYOUT, path_content_no_dir, L("page/single.html", "single.html")},
{SIMPLE_PAGE_NOLAYOUT, path_one_directory, L("fub/single.html", "single.html")}, {SIMPLE_PAGE_NOLAYOUT, path_one_directory, L("fub/single.html", "single.html")},
{SIMPLE_PAGE_NOLAYOUT, path_no_directory, L("page/single.html", "single.html")}, {SIMPLE_PAGE_NOLAYOUT, path_no_directory, L("page/single.html", "single.html")},
{SIMPLE_PAGE_LAYOUT_FOOBAR, path_content_two_dir, L("dub/sub/foobar.html", "dub/foobar.html", "foobar.html")}, {SIMPLE_PAGE_LAYOUT_FOOBAR, path_content_two_dir, L("dub/sub/foobar.html", "dub/foobar.html", "foobar.html")},
{SIMPLE_PAGE_LAYOUT_FOOBAR, path_content_one_dir, L("gub/foobar.html", "foobar.html")}, {SIMPLE_PAGE_LAYOUT_FOOBAR, path_content_one_dir, L("gub/foobar.html", "foobar.html")},
{SIMPLE_PAGE_LAYOUT_FOOBAR, path_one_directory, L("fub/foobar.html", "foobar.html")}, {SIMPLE_PAGE_LAYOUT_FOOBAR, path_one_directory, L("fub/foobar.html", "foobar.html")},
{SIMPLE_PAGE_LAYOUT_FOOBAR, path_no_directory, L("page/foobar.html", "foobar.html")}, {SIMPLE_PAGE_LAYOUT_FOOBAR, path_no_directory, L("page/foobar.html", "foobar.html")},
{SIMPLE_PAGE_TYPE_FOOBAR, path_content_two_dir, L("foobar/single.html", "single.html")}, {SIMPLE_PAGE_TYPE_FOOBAR, path_content_two_dir, L("foobar/single.html", "single.html")},
{SIMPLE_PAGE_TYPE_FOOBAR, path_content_one_dir, L("foobar/single.html", "single.html")}, {SIMPLE_PAGE_TYPE_FOOBAR, path_content_one_dir, L("foobar/single.html", "single.html")},
{SIMPLE_PAGE_TYPE_FOOBAR, path_content_no_dir, L("foobar/single.html", "single.html")}, {SIMPLE_PAGE_TYPE_FOOBAR, path_content_no_dir, L("foobar/single.html", "single.html")},
{SIMPLE_PAGE_TYPE_FOOBAR, path_one_directory, L("foobar/single.html", "single.html")}, {SIMPLE_PAGE_TYPE_FOOBAR, path_one_directory, L("foobar/single.html", "single.html")},
{SIMPLE_PAGE_TYPE_FOOBAR, path_no_directory, L("foobar/single.html", "single.html")}, {SIMPLE_PAGE_TYPE_FOOBAR, path_no_directory, L("foobar/single.html", "single.html")},
{SIMPLE_PAGE_TYPE_LAYOUT, path_content_two_dir, L("barfoo/buzfoo.html", "buzfoo.html")}, {SIMPLE_PAGE_TYPE_LAYOUT, path_content_two_dir, L("barfoo/buzfoo.html", "buzfoo.html")},
{SIMPLE_PAGE_TYPE_LAYOUT, path_content_one_dir, L("barfoo/buzfoo.html", "buzfoo.html")}, {SIMPLE_PAGE_TYPE_LAYOUT, path_content_one_dir, L("barfoo/buzfoo.html", "buzfoo.html")},
{SIMPLE_PAGE_TYPE_LAYOUT, path_content_no_dir, L("barfoo/buzfoo.html", "buzfoo.html")}, {SIMPLE_PAGE_TYPE_LAYOUT, path_content_no_dir, L("barfoo/buzfoo.html", "buzfoo.html")},
{SIMPLE_PAGE_TYPE_LAYOUT, path_one_directory, L("barfoo/buzfoo.html", "buzfoo.html")}, {SIMPLE_PAGE_TYPE_LAYOUT, path_one_directory, L("barfoo/buzfoo.html", "buzfoo.html")},
{SIMPLE_PAGE_TYPE_LAYOUT, path_no_directory, L("barfoo/buzfoo.html", "buzfoo.html")}, {SIMPLE_PAGE_TYPE_LAYOUT, path_no_directory, L("barfoo/buzfoo.html", "buzfoo.html")},
} }
for _, test := range tests { for _, test := range tests {
p, err := ReadFrom(strings.NewReader(test.content), test.path) p, err := ReadFrom(strings.NewReader(test.content), test.path)
if err != nil { if err != nil {
t.Fatalf("Unable to parse content:\n%s\n", test.content) t.Fatalf("Unable to parse content:\n%s\n", test.content)
} }
if !listEqual(p.Layout(), test.expectedLayout) { if !listEqual(p.Layout(), test.expectedLayout) {
t.Errorf("Layout mismatch. Expected: %s, got: %s", test.expectedLayout, p.Layout()) t.Errorf("Layout mismatch. Expected: %s, got: %s", test.expectedLayout, p.Layout())
} }
} }
} }
func listEqual(left, right []string) bool { func listEqual(left, right []string) bool {
if len(left) != len(right) { if len(left) != len(right) {
return false return false
} }
for i := range left { for i := range left {
if left[i] != right[i] { if left[i] != right[i] {
return false return false
} }
} }
return true return true
} }

View File

@ -14,12 +14,12 @@
package hugolib package hugolib
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/spf13/hugo/template/bundle" "github.com/spf13/hugo/template/bundle"
"html/template" "html/template"
"strings" "strings"
"unicode" "unicode"
) )
var _ = fmt.Println var _ = fmt.Println
@ -27,201 +27,201 @@ var _ = fmt.Println
type ShortcodeFunc func([]string) string type ShortcodeFunc func([]string) string
type Shortcode struct { type Shortcode struct {
Name string Name string
Func ShortcodeFunc Func ShortcodeFunc
} }
type ShortcodeWithPage struct { type ShortcodeWithPage struct {
Params interface{} Params interface{}
Inner template.HTML Inner template.HTML
Page *Page Page *Page
} }
type Shortcodes map[string]ShortcodeFunc type Shortcodes map[string]ShortcodeFunc
func ShortcodesHandle(stringToParse string, p *Page, t bundle.Template) string { func ShortcodesHandle(stringToParse string, p *Page, t bundle.Template) string {
leadStart := strings.Index(stringToParse, `{{%`) leadStart := strings.Index(stringToParse, `{{%`)
if leadStart >= 0 { if leadStart >= 0 {
leadEnd := strings.Index(stringToParse[leadStart:], `%}}`) + leadStart leadEnd := strings.Index(stringToParse[leadStart:], `%}}`) + leadStart
if leadEnd > leadStart { if leadEnd > leadStart {
name, par := SplitParams(stringToParse[leadStart+3 : leadEnd]) name, par := SplitParams(stringToParse[leadStart+3 : leadEnd])
tmpl := GetTemplate(name, t) tmpl := GetTemplate(name, t)
if tmpl == nil { if tmpl == nil {
return stringToParse return stringToParse
} }
params := Tokenize(par) params := Tokenize(par)
// Always look for closing tag. // Always look for closing tag.
endStart, endEnd := FindEnd(stringToParse[leadEnd:], name) endStart, endEnd := FindEnd(stringToParse[leadEnd:], name)
var data = &ShortcodeWithPage{Params: params, Page: p} var data = &ShortcodeWithPage{Params: params, Page: p}
if endStart > 0 { if endStart > 0 {
s := stringToParse[leadEnd+3 : leadEnd+endStart] s := stringToParse[leadEnd+3 : leadEnd+endStart]
data.Inner = template.HTML(CleanP(ShortcodesHandle(s, p, t))) data.Inner = template.HTML(CleanP(ShortcodesHandle(s, p, t)))
remainder := CleanP(stringToParse[leadEnd+endEnd:]) remainder := CleanP(stringToParse[leadEnd+endEnd:])
return CleanP(stringToParse[:leadStart]) + return CleanP(stringToParse[:leadStart]) +
ShortcodeRender(tmpl, data) + ShortcodeRender(tmpl, data) +
CleanP(ShortcodesHandle(remainder, p, t)) CleanP(ShortcodesHandle(remainder, p, t))
} }
return CleanP(stringToParse[:leadStart]) + return CleanP(stringToParse[:leadStart]) +
ShortcodeRender(tmpl, data) + ShortcodeRender(tmpl, data) +
CleanP(ShortcodesHandle(stringToParse[leadEnd+3:], p, CleanP(ShortcodesHandle(stringToParse[leadEnd+3:], p,
t)) t))
} }
} }
return stringToParse return stringToParse
} }
// Clean up odd behavior when closing tag is on first line // Clean up odd behavior when closing tag is on first line
// or opening tag is on the last line due to extra line in markdown file // or opening tag is on the last line due to extra line in markdown file
func CleanP(str string) string { func CleanP(str string) string {
if strings.HasSuffix(strings.TrimSpace(str), "<p>") { if strings.HasSuffix(strings.TrimSpace(str), "<p>") {
idx := strings.LastIndex(str, "<p>") idx := strings.LastIndex(str, "<p>")
str = str[:idx] str = str[:idx]
} }
if strings.HasPrefix(strings.TrimSpace(str), "</p>") { if strings.HasPrefix(strings.TrimSpace(str), "</p>") {
str = str[strings.Index(str, "</p>")+5:] str = str[strings.Index(str, "</p>")+5:]
} }
return str return str
} }
func FindEnd(str string, name string) (int, int) { func FindEnd(str string, name string) (int, int) {
var endPos int var endPos int
var startPos int var startPos int
var try []string var try []string
try = append(try, "{{% /"+name+" %}}") try = append(try, "{{% /"+name+" %}}")
try = append(try, "{{% /"+name+"%}}") try = append(try, "{{% /"+name+"%}}")
try = append(try, "{{%/"+name+"%}}") try = append(try, "{{%/"+name+"%}}")
try = append(try, "{{%/"+name+" %}}") try = append(try, "{{%/"+name+" %}}")
lowest := len(str) lowest := len(str)
for _, x := range try { for _, x := range try {
start := strings.Index(str, x) start := strings.Index(str, x)
if start < lowest && start > 0 { if start < lowest && start > 0 {
startPos = start startPos = start
endPos = startPos + len(x) endPos = startPos + len(x)
} }
} }
return startPos, endPos return startPos, endPos
} }
func GetTemplate(name string, t bundle.Template) *template.Template { func GetTemplate(name string, t bundle.Template) *template.Template {
if x := t.Lookup("shortcodes/" + name + ".html"); x != nil { if x := t.Lookup("shortcodes/" + name + ".html"); x != nil {
return x return x
} }
return t.Lookup("_internal/shortcodes/" + name + ".html") return t.Lookup("_internal/shortcodes/" + name + ".html")
} }
func StripShortcodes(stringToParse string) string { func StripShortcodes(stringToParse string) string {
posStart := strings.Index(stringToParse, "{{%") posStart := strings.Index(stringToParse, "{{%")
if posStart > 0 { if posStart > 0 {
posEnd := strings.Index(stringToParse[posStart:], "%}}") + posStart posEnd := strings.Index(stringToParse[posStart:], "%}}") + posStart
if posEnd > posStart { if posEnd > posStart {
newString := stringToParse[:posStart] + StripShortcodes(stringToParse[posEnd+3:]) newString := stringToParse[:posStart] + StripShortcodes(stringToParse[posEnd+3:])
return newString return newString
} }
} }
return stringToParse return stringToParse
} }
func Tokenize(in string) interface{} { func Tokenize(in string) interface{} {
first := strings.Fields(in) first := strings.Fields(in)
var final = make([]string, 0) var final = make([]string, 0)
// if don't need to parse, don't parse. // if don't need to parse, don't parse.
if strings.Index(in, " ") < 0 && strings.Index(in, "=") < 1 { if strings.Index(in, " ") < 0 && strings.Index(in, "=") < 1 {
return append(final, in) return append(final, in)
} }
var keys = make([]string, 0) var keys = make([]string, 0)
inQuote := false inQuote := false
start := 0 start := 0
for i, v := range first { for i, v := range first {
index := strings.Index(v, "=") index := strings.Index(v, "=")
if !inQuote { if !inQuote {
if index > 1 { if index > 1 {
keys = append(keys, v[:index]) keys = append(keys, v[:index])
v = v[index+1:] v = v[index+1:]
} }
} }
// Adjusted to handle htmlencoded and non htmlencoded input // Adjusted to handle htmlencoded and non htmlencoded input
if !strings.HasPrefix(v, "&ldquo;") && !strings.HasPrefix(v, "\"") && !inQuote { if !strings.HasPrefix(v, "&ldquo;") && !strings.HasPrefix(v, "\"") && !inQuote {
final = append(final, v) final = append(final, v)
} else if inQuote && (strings.HasSuffix(v, "&rdquo;") || } else if inQuote && (strings.HasSuffix(v, "&rdquo;") ||
strings.HasSuffix(v, "\"")) && !strings.HasSuffix(v, "\\\"") { strings.HasSuffix(v, "\"")) && !strings.HasSuffix(v, "\\\"") {
if strings.HasSuffix(v, "\"") { if strings.HasSuffix(v, "\"") {
first[i] = v[:len(v)-1] first[i] = v[:len(v)-1]
} else { } else {
first[i] = v[:len(v)-7] first[i] = v[:len(v)-7]
} }
final = append(final, strings.Join(first[start:i+1], " ")) final = append(final, strings.Join(first[start:i+1], " "))
inQuote = false inQuote = false
} else if (strings.HasPrefix(v, "&ldquo;") || } else if (strings.HasPrefix(v, "&ldquo;") ||
strings.HasPrefix(v, "\"")) && !inQuote { strings.HasPrefix(v, "\"")) && !inQuote {
if strings.HasSuffix(v, "&rdquo;") || strings.HasSuffix(v, if strings.HasSuffix(v, "&rdquo;") || strings.HasSuffix(v,
"\"") { "\"") {
if strings.HasSuffix(v, "\"") { if strings.HasSuffix(v, "\"") {
if len(v) > 1 { if len(v) > 1 {
final = append(final, v[1:len(v)-1]) final = append(final, v[1:len(v)-1])
} else { } else {
final = append(final, "") final = append(final, "")
} }
} else { } else {
final = append(final, v[7:len(v)-7]) final = append(final, v[7:len(v)-7])
} }
} else { } else {
start = i start = i
if strings.HasPrefix(v, "\"") { if strings.HasPrefix(v, "\"") {
first[i] = v[1:] first[i] = v[1:]
} else { } else {
first[i] = v[7:] first[i] = v[7:]
} }
inQuote = true inQuote = true
} }
} }
// No closing "... just make remainder the final token // No closing "... just make remainder the final token
if inQuote && i == len(first) { if inQuote && i == len(first) {
final = append(final, first[start:]...) final = append(final, first[start:]...)
} }
} }
if len(keys) > 0 && (len(keys) != len(final)) { if len(keys) > 0 && (len(keys) != len(final)) {
panic("keys and final different lengths") panic("keys and final different lengths")
} }
if len(keys) > 0 { if len(keys) > 0 {
var m = make(map[string]string) var m = make(map[string]string)
for i, k := range keys { for i, k := range keys {
m[k] = final[i] m[k] = final[i]
} }
return m return m
} }
return final return final
} }
func SplitParams(in string) (name string, par2 string) { func SplitParams(in string) (name string, par2 string) {
i := strings.IndexFunc(strings.TrimSpace(in), unicode.IsSpace) i := strings.IndexFunc(strings.TrimSpace(in), unicode.IsSpace)
if i < 1 { if i < 1 {
return strings.TrimSpace(in), "" return strings.TrimSpace(in), ""
} }
return strings.TrimSpace(in[:i+1]), strings.TrimSpace(in[i+1:]) return strings.TrimSpace(in[:i+1]), strings.TrimSpace(in[i+1:])
} }
func ShortcodeRender(tmpl *template.Template, data *ShortcodeWithPage) string { func ShortcodeRender(tmpl *template.Template, data *ShortcodeWithPage) string {
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
err := tmpl.Execute(buffer, data) err := tmpl.Execute(buffer, data)
if err != nil { if err != nil {
fmt.Println("error processing shortcode", tmpl.Name(), "\n ERR:", err) fmt.Println("error processing shortcode", tmpl.Name(), "\n ERR:", err)
} }
return buffer.String() return buffer.String()
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +1,37 @@
package hugolib package hugolib
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/spf13/hugo/source" "github.com/spf13/hugo/source"
"github.com/spf13/hugo/target" "github.com/spf13/hugo/target"
"html/template" "html/template"
"io" "io"
"strings" "strings"
"testing" "testing"
) )
const ( const (
TEMPLATE_TITLE = "{{ .Title }}" TEMPLATE_TITLE = "{{ .Title }}"
PAGE_SIMPLE_TITLE = `--- PAGE_SIMPLE_TITLE = `---
title: simple template title: simple template
--- ---
content` content`
TEMPLATE_MISSING_FUNC = "{{ .Title | funcdoesnotexists }}" TEMPLATE_MISSING_FUNC = "{{ .Title | funcdoesnotexists }}"
TEMPLATE_FUNC = "{{ .Title | urlize }}" TEMPLATE_FUNC = "{{ .Title | urlize }}"
TEMPLATE_CONTENT = "{{ .Content }}" TEMPLATE_CONTENT = "{{ .Content }}"
TEMPLATE_DATE = "{{ .Date }}" TEMPLATE_DATE = "{{ .Date }}"
INVALID_TEMPLATE_FORMAT_DATE = "{{ .Date.Format time.RFC3339 }}" INVALID_TEMPLATE_FORMAT_DATE = "{{ .Date.Format time.RFC3339 }}"
TEMPLATE_WITH_URL_REL = "<a href=\"foobar.jpg\">Going</a>" TEMPLATE_WITH_URL_REL = "<a href=\"foobar.jpg\">Going</a>"
TEMPLATE_WITH_URL_ABS = "<a href=\"/foobar.jpg\">Going</a>" TEMPLATE_WITH_URL_ABS = "<a href=\"/foobar.jpg\">Going</a>"
PAGE_URL_SPECIFIED = `--- PAGE_URL_SPECIFIED = `---
title: simple template title: simple template
url: "mycategory/my-whatever-content/" url: "mycategory/my-whatever-content/"
--- ---
content` content`
PAGE_WITH_MD = `--- PAGE_WITH_MD = `---
title: page with md title: page with md
--- ---
# heading 1 # heading 1
@ -42,306 +42,306 @@ more text
) )
func pageMust(p *Page, err error) *Page { func pageMust(p *Page, err error) *Page {
if err != nil { if err != nil {
panic(err) panic(err)
} }
return p return p
} }
func TestDegenerateRenderThingMissingTemplate(t *testing.T) { func TestDegenerateRenderThingMissingTemplate(t *testing.T) {
p, _ := ReadFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md") p, _ := ReadFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md")
p.Convert() p.Convert()
s := new(Site) s := new(Site)
s.prepTemplates() s.prepTemplates()
err := s.renderThing(p, "foobar", nil) err := s.renderThing(p, "foobar", nil)
if err == nil { if err == nil {
t.Errorf("Expected err to be returned when missing the template.") t.Errorf("Expected err to be returned when missing the template.")
} }
} }
func TestAddInvalidTemplate(t *testing.T) { func TestAddInvalidTemplate(t *testing.T) {
s := new(Site) s := new(Site)
s.prepTemplates() s.prepTemplates()
err := s.addTemplate("missing", TEMPLATE_MISSING_FUNC) err := s.addTemplate("missing", TEMPLATE_MISSING_FUNC)
if err == nil { if err == nil {
t.Fatalf("Expecting the template to return an error") t.Fatalf("Expecting the template to return an error")
} }
} }
type nopCloser struct { type nopCloser struct {
io.Writer io.Writer
} }
func (nopCloser) Close() error { return nil } func (nopCloser) Close() error { return nil }
func NopCloser(w io.Writer) io.WriteCloser { func NopCloser(w io.Writer) io.WriteCloser {
return nopCloser{w} return nopCloser{w}
} }
func matchRender(t *testing.T, s *Site, p *Page, tmplName string, expected string) { func matchRender(t *testing.T, s *Site, p *Page, tmplName string, expected string) {
content := new(bytes.Buffer) content := new(bytes.Buffer)
err := s.renderThing(p, tmplName, NopCloser(content)) err := s.renderThing(p, tmplName, NopCloser(content))
if err != nil { if err != nil {
t.Fatalf("Unable to render template.") t.Fatalf("Unable to render template.")
} }
if string(content.Bytes()) != expected { if string(content.Bytes()) != expected {
t.Fatalf("Content did not match expected: %s. got: %s", expected, content) t.Fatalf("Content did not match expected: %s. got: %s", expected, content)
} }
} }
func TestRenderThing(t *testing.T) { func TestRenderThing(t *testing.T) {
tests := []struct { tests := []struct {
content string content string
template string template string
expected string expected string
}{ }{
{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, "\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"}, {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"},
} }
s := new(Site) s := new(Site)
s.prepTemplates() s.prepTemplates()
for i, test := range tests { for i, test := range tests {
p, err := ReadFrom(strings.NewReader(test.content), "content/a/file.md") p, err := ReadFrom(strings.NewReader(test.content), "content/a/file.md")
p.Convert() p.Convert()
if err != nil { if err != nil {
t.Fatalf("Error parsing buffer: %s", err) t.Fatalf("Error parsing buffer: %s", err)
} }
templateName := fmt.Sprintf("foobar%d", i) templateName := fmt.Sprintf("foobar%d", i)
err = s.addTemplate(templateName, test.template) err = s.addTemplate(templateName, test.template)
if err != nil { if err != nil {
t.Fatalf("Unable to add template") t.Fatalf("Unable to add template")
} }
p.Content = template.HTML(p.Content) p.Content = template.HTML(p.Content)
html := new(bytes.Buffer) html := new(bytes.Buffer)
err = s.renderThing(p, templateName, NopCloser(html)) err = s.renderThing(p, templateName, NopCloser(html))
if err != nil { if err != nil {
t.Errorf("Unable to render html: %s", err) t.Errorf("Unable to render html: %s", err)
} }
if string(html.Bytes()) != test.expected { if string(html.Bytes()) != test.expected {
t.Errorf("Content does not match.\nExpected\n\t'%q'\ngot\n\t'%q'", test.expected, html) t.Errorf("Content does not match.\nExpected\n\t'%q'\ngot\n\t'%q'", test.expected, html)
} }
} }
} }
func HTML(in string) string { func HTML(in string) string {
return in return in
} }
func TestRenderThingOrDefault(t *testing.T) { func TestRenderThingOrDefault(t *testing.T) {
tests := []struct { tests := []struct {
content string content string
missing bool missing bool
template string template string
expected string expected string
}{ }{
{PAGE_SIMPLE_TITLE, true, TEMPLATE_TITLE, HTML("simple template")}, {PAGE_SIMPLE_TITLE, true, TEMPLATE_TITLE, HTML("simple template")},
{PAGE_SIMPLE_TITLE, true, TEMPLATE_FUNC, HTML("simple-template")}, {PAGE_SIMPLE_TITLE, true, TEMPLATE_FUNC, HTML("simple-template")},
{PAGE_SIMPLE_TITLE, false, TEMPLATE_TITLE, HTML("simple template")}, {PAGE_SIMPLE_TITLE, false, TEMPLATE_TITLE, HTML("simple template")},
{PAGE_SIMPLE_TITLE, false, TEMPLATE_FUNC, HTML("simple-template")}, {PAGE_SIMPLE_TITLE, false, TEMPLATE_FUNC, HTML("simple-template")},
} }
files := make(map[string][]byte) files := make(map[string][]byte)
target := &target.InMemoryTarget{Files: files} target := &target.InMemoryTarget{Files: files}
s := &Site{ s := &Site{
Target: target, Target: target,
} }
s.prepTemplates() s.prepTemplates()
for i, test := range tests { for i, test := range tests {
p, err := ReadFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md") p, err := ReadFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md")
if err != nil { if err != nil {
t.Fatalf("Error parsing buffer: %s", err) t.Fatalf("Error parsing buffer: %s", err)
} }
templateName := fmt.Sprintf("default%d", i) templateName := fmt.Sprintf("default%d", i)
err = s.addTemplate(templateName, test.template) err = s.addTemplate(templateName, test.template)
if err != nil { if err != nil {
t.Fatalf("Unable to add template") t.Fatalf("Unable to add template")
} }
var err2 error var err2 error
if test.missing { if test.missing {
err2 = s.render(p, "out", "missing", templateName) err2 = s.render(p, "out", "missing", templateName)
} else { } else {
err2 = s.render(p, "out", templateName, "missing_default") err2 = s.render(p, "out", templateName, "missing_default")
} }
if err2 != nil { if err2 != nil {
t.Errorf("Unable to render html: %s", err) t.Errorf("Unable to render html: %s", err)
} }
if string(files["out"]) != test.expected { if string(files["out"]) != test.expected {
t.Errorf("Content does not match. Expected '%s', got '%s'", test.expected, files["out"]) t.Errorf("Content does not match. Expected '%s', got '%s'", test.expected, files["out"])
} }
} }
} }
func TestTargetPath(t *testing.T) { func TestTargetPath(t *testing.T) {
tests := []struct { tests := []struct {
doc string doc string
content string content string
expectedOutFile string expectedOutFile string
expectedSection string expectedSection string
}{ }{
{"content/a/file.md", PAGE_URL_SPECIFIED, "mycategory/my-whatever-content/index.html", "a"}, {"content/a/file.md", PAGE_URL_SPECIFIED, "mycategory/my-whatever-content/index.html", "a"},
{"content/x/y/deepfile.md", SIMPLE_PAGE, "x/y/deepfile.html", "x/y"}, {"content/x/y/deepfile.md", SIMPLE_PAGE, "x/y/deepfile.html", "x/y"},
{"content/x/y/z/deeperfile.md", SIMPLE_PAGE, "x/y/z/deeperfile.html", "x/y/z"}, {"content/x/y/z/deeperfile.md", SIMPLE_PAGE, "x/y/z/deeperfile.html", "x/y/z"},
{"content/b/file.md", SIMPLE_PAGE, "b/file.html", "b"}, {"content/b/file.md", SIMPLE_PAGE, "b/file.html", "b"},
{"a/file.md", SIMPLE_PAGE, "a/file.html", "a"}, {"a/file.md", SIMPLE_PAGE, "a/file.html", "a"},
{"file.md", SIMPLE_PAGE, "file.html", ""}, {"file.md", SIMPLE_PAGE, "file.html", ""},
} }
if true { if true {
return return
} }
for _, test := range tests { for _, test := range tests {
s := &Site{ s := &Site{
Config: Config{ContentDir: "content"}, Config: Config{ContentDir: "content"},
} }
p := pageMust(ReadFrom(strings.NewReader(test.content), s.Config.GetAbsPath(test.doc))) p := pageMust(ReadFrom(strings.NewReader(test.content), s.Config.GetAbsPath(test.doc)))
expected := test.expectedOutFile expected := test.expectedOutFile
if p.TargetPath() != expected { if p.TargetPath() != expected {
t.Errorf("%s => OutFile expected: '%s', got: '%s'", test.doc, expected, p.TargetPath()) t.Errorf("%s => OutFile expected: '%s', got: '%s'", test.doc, expected, p.TargetPath())
} }
if p.Section != test.expectedSection { if p.Section != test.expectedSection {
t.Errorf("%s => p.Section expected: %s, got: %s", test.doc, test.expectedSection, p.Section) t.Errorf("%s => p.Section expected: %s, got: %s", test.doc, test.expectedSection, p.Section)
} }
} }
} }
func TestSkipRender(t *testing.T) { func TestSkipRender(t *testing.T) {
files := make(map[string][]byte) files := make(map[string][]byte)
target := &target.InMemoryTarget{Files: files} target := &target.InMemoryTarget{Files: files}
sources := []source.ByteSource{ sources := []source.ByteSource{
{"sect/doc1.html", []byte("---\nmarkup: markdown\n---\n# title\nsome *content*"), "sect"}, {"sect/doc1.html", []byte("---\nmarkup: markdown\n---\n# title\nsome *content*"), "sect"},
{"sect/doc2.html", []byte("<!doctype html><html><body>more content</body></html>"), "sect"}, {"sect/doc2.html", []byte("<!doctype html><html><body>more content</body></html>"), "sect"},
{"sect/doc3.md", []byte("# doc3\n*some* content"), "sect"}, {"sect/doc3.md", []byte("# doc3\n*some* content"), "sect"},
{"sect/doc4.md", []byte("---\ntitle: doc4\n---\n# doc4\n*some content*"), "sect"}, {"sect/doc4.md", []byte("---\ntitle: doc4\n---\n# doc4\n*some content*"), "sect"},
{"sect/doc5.html", []byte("<!doctype html><html>{{ template \"head\" }}<body>body5</body></html>"), "sect"}, {"sect/doc5.html", []byte("<!doctype html><html>{{ template \"head\" }}<body>body5</body></html>"), "sect"},
{"sect/doc6.html", []byte("<!doctype html><html>{{ template \"head_abs\" }}<body>body5</body></html>"), "sect"}, {"sect/doc6.html", []byte("<!doctype html><html>{{ template \"head_abs\" }}<body>body5</body></html>"), "sect"},
{"doc7.html", []byte("<html><body>doc7 content</body></html>"), ""}, {"doc7.html", []byte("<html><body>doc7 content</body></html>"), ""},
{"sect/doc8.html", []byte("---\nmarkup: md\n---\n# title\nsome *content*"), "sect"}, {"sect/doc8.html", []byte("---\nmarkup: md\n---\n# title\nsome *content*"), "sect"},
} }
s := &Site{ s := &Site{
Target: target, Target: target,
Config: Config{ Config: Config{
Verbose: true, Verbose: true,
BaseUrl: "http://auth/bub", BaseUrl: "http://auth/bub",
CanonifyUrls: true, CanonifyUrls: true,
}, },
Source: &source.InMemorySource{sources}, Source: &source.InMemorySource{sources},
} }
s.initializeSiteInfo() s.initializeSiteInfo()
s.prepTemplates() s.prepTemplates()
must(s.addTemplate("_default/single.html", "{{.Content}}")) must(s.addTemplate("_default/single.html", "{{.Content}}"))
must(s.addTemplate("head", "<head><script src=\"script.js\"></script></head>")) must(s.addTemplate("head", "<head><script src=\"script.js\"></script></head>"))
must(s.addTemplate("head_abs", "<head><script src=\"/script.js\"></script></head>")) must(s.addTemplate("head_abs", "<head><script src=\"/script.js\"></script></head>"))
if err := s.CreatePages(); err != nil { if err := s.CreatePages(); err != nil {
t.Fatalf("Unable to create pages: %s", err) t.Fatalf("Unable to create pages: %s", err)
} }
if err := s.BuildSiteMeta(); err != nil { if err := s.BuildSiteMeta(); err != nil {
t.Fatalf("Unable to build site metadata: %s", err) t.Fatalf("Unable to build site metadata: %s", err)
} }
if err := s.RenderPages(); err != nil { if err := s.RenderPages(); err != nil {
t.Fatalf("Unable to render pages. %s", err) t.Fatalf("Unable to render pages. %s", err)
} }
tests := []struct { tests := []struct {
doc string doc string
expected string expected string
}{ }{
{"sect/doc1.html", "\n\n<h1 id=\"toc_0\">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", "\n\n<h1 id=\"toc_0\">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", "\n\n<h1 id=\"toc_0\">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", "\n\n<h1 id=\"toc_0\">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 {
content, ok := target.Files[test.doc] content, ok := target.Files[test.doc]
if !ok { if !ok {
t.Fatalf("Did not find %s in target. %v", test.doc, target.Files) t.Fatalf("Did not find %s in target. %v", test.doc, target.Files)
} }
if !bytes.Equal(content, []byte(test.expected)) { if !bytes.Equal(content, []byte(test.expected)) {
t.Errorf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, string(content)) t.Errorf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, string(content))
} }
} }
} }
func TestAbsUrlify(t *testing.T) { func TestAbsUrlify(t *testing.T) {
files := make(map[string][]byte) files := make(map[string][]byte)
target := &target.InMemoryTarget{Files: files} target := &target.InMemoryTarget{Files: files}
sources := []source.ByteSource{ sources := []source.ByteSource{
{"sect/doc1.html", []byte("<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"), "sect"}, {"sect/doc1.html", []byte("<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"), "sect"},
{"content/blue/doc2.html", []byte("---\nf: t\n---\n<!doctype html><html><body>more content</body></html>"), "blue"}, {"content/blue/doc2.html", []byte("---\nf: t\n---\n<!doctype html><html><body>more content</body></html>"), "blue"},
} }
for _, canonify := range []bool{true, false} { for _, canonify := range []bool{true, false} {
s := &Site{ s := &Site{
Target: target, Target: target,
Config: Config{ Config: Config{
BaseUrl: "http://auth/bub", BaseUrl: "http://auth/bub",
CanonifyUrls: canonify, CanonifyUrls: canonify,
}, },
Source: &source.InMemorySource{sources}, Source: &source.InMemorySource{sources},
} }
t.Logf("Rendering with BaseUrl %q and CanonifyUrls set %v", s.Config.BaseUrl, canonify) t.Logf("Rendering with BaseUrl %q and CanonifyUrls set %v", s.Config.BaseUrl, canonify)
s.initializeSiteInfo() s.initializeSiteInfo()
s.prepTemplates() s.prepTemplates()
must(s.addTemplate("blue/single.html", TEMPLATE_WITH_URL_ABS)) must(s.addTemplate("blue/single.html", TEMPLATE_WITH_URL_ABS))
if err := s.CreatePages(); err != nil { if err := s.CreatePages(); err != nil {
t.Fatalf("Unable to create pages: %s", err) t.Fatalf("Unable to create pages: %s", err)
} }
if err := s.BuildSiteMeta(); err != nil { if err := s.BuildSiteMeta(); err != nil {
t.Fatalf("Unable to build site metadata: %s", err) t.Fatalf("Unable to build site metadata: %s", err)
} }
if err := s.RenderPages(); err != nil { if err := s.RenderPages(); err != nil {
t.Fatalf("Unable to render pages. %s", err) t.Fatalf("Unable to render pages. %s", err)
} }
tests := []struct { tests := []struct {
file, expected string file, expected string
}{ }{
{"content/blue/doc2.html", "<a href=\"http://auth/bub/foobar.jpg\">Going</a>"}, {"content/blue/doc2.html", "<a href=\"http://auth/bub/foobar.jpg\">Going</a>"},
{"sect/doc1.html", "<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"}, {"sect/doc1.html", "<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"},
} }
for _, test := range tests { for _, test := range tests {
content, ok := target.Files[test.file] content, ok := target.Files[test.file]
if !ok { if !ok {
t.Fatalf("Unable to locate rendered content: %s", test.file) t.Fatalf("Unable to locate rendered content: %s", test.file)
} }
expected := test.expected expected := test.expected
if !canonify { if !canonify {
expected = strings.Replace(expected, s.Config.BaseUrl, "", -1) expected = strings.Replace(expected, s.Config.BaseUrl, "", -1)
} }
if string(content) != expected { if string(content) != expected {
t.Errorf("AbsUrlify content expected:\n%q\ngot\n%q", expected, string(content)) t.Errorf("AbsUrlify content expected:\n%q\ngot\n%q", expected, string(content))
} }
} }
} }
} }
var WEIGHTED_PAGE_1 = []byte(`+++ var WEIGHTED_PAGE_1 = []byte(`+++
@ -371,58 +371,58 @@ date = "2012-01-01"
Front Matter with Ordered Pages 4. This is longer content`) Front Matter with Ordered Pages 4. This is longer content`)
var WEIGHTED_SOURCES = []source.ByteSource{ var WEIGHTED_SOURCES = []source.ByteSource{
{"sect/doc1.md", WEIGHTED_PAGE_1, "sect"}, {"sect/doc1.md", WEIGHTED_PAGE_1, "sect"},
{"sect/doc2.md", WEIGHTED_PAGE_2, "sect"}, {"sect/doc2.md", WEIGHTED_PAGE_2, "sect"},
{"sect/doc3.md", WEIGHTED_PAGE_3, "sect"}, {"sect/doc3.md", WEIGHTED_PAGE_3, "sect"},
{"sect/doc4.md", WEIGHTED_PAGE_4, "sect"}, {"sect/doc4.md", WEIGHTED_PAGE_4, "sect"},
} }
func TestOrderedPages(t *testing.T) { func TestOrderedPages(t *testing.T) {
files := make(map[string][]byte) files := make(map[string][]byte)
target := &target.InMemoryTarget{Files: files} target := &target.InMemoryTarget{Files: files}
s := &Site{ s := &Site{
Target: target, Target: target,
Config: Config{BaseUrl: "http://auth/bub/"}, Config: Config{BaseUrl: "http://auth/bub/"},
Source: &source.InMemorySource{WEIGHTED_SOURCES}, Source: &source.InMemorySource{WEIGHTED_SOURCES},
} }
s.initializeSiteInfo() s.initializeSiteInfo()
if err := s.CreatePages(); err != nil { if err := s.CreatePages(); err != nil {
t.Fatalf("Unable to create pages: %s", err) t.Fatalf("Unable to create pages: %s", err)
} }
if err := s.BuildSiteMeta(); err != nil { if err := s.BuildSiteMeta(); err != nil {
t.Fatalf("Unable to build site metadata: %s", err) t.Fatalf("Unable to build site metadata: %s", err)
} }
if s.Sections["sect"][0].Weight != 2 || s.Sections["sect"][3].Weight != 6 { if s.Sections["sect"][0].Weight != 2 || s.Sections["sect"][3].Weight != 6 {
t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", 2, s.Sections["sect"][0].Weight) t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", 2, s.Sections["sect"][0].Weight)
} }
if s.Sections["sect"][1].Page.Title != "Three" || s.Sections["sect"][2].Page.Title != "Four" { if s.Sections["sect"][1].Page.Title != "Three" || s.Sections["sect"][2].Page.Title != "Four" {
t.Errorf("Pages in unexpected order. Second should be '%s', got '%s'", "Three", s.Sections["sect"][1].Page.Title) t.Errorf("Pages in unexpected order. Second should be '%s', got '%s'", "Three", s.Sections["sect"][1].Page.Title)
} }
bydate := s.Pages.ByDate() bydate := s.Pages.ByDate()
if bydate[0].Title != "One" { if bydate[0].Title != "One" {
t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bydate[0].Title) t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bydate[0].Title)
} }
rev := bydate.Reverse() rev := bydate.Reverse()
if rev[0].Title != "Three" { if rev[0].Title != "Three" {
t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Three", rev[0].Title) t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Three", rev[0].Title)
} }
bylength := s.Pages.ByLength() bylength := s.Pages.ByLength()
if bylength[0].Title != "One" { if bylength[0].Title != "One" {
t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bylength[0].Title) t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bylength[0].Title)
} }
rbylength := bylength.Reverse() rbylength := bylength.Reverse()
if rbylength[0].Title != "Four" { if rbylength[0].Title != "Four" {
t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Four", rbylength[0].Title) t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Four", rbylength[0].Title)
} }
} }
var PAGE_WITH_WEIGHTED_INDEXES_2 = []byte(`+++ var PAGE_WITH_WEIGHTED_INDEXES_2 = []byte(`+++
@ -455,41 +455,41 @@ date = 2010-05-27T07:32:00Z
Front Matter with weighted tags and categories`) Front Matter with weighted tags and categories`)
func TestWeightedIndexes(t *testing.T) { func TestWeightedIndexes(t *testing.T) {
files := make(map[string][]byte) files := make(map[string][]byte)
target := &target.InMemoryTarget{Files: files} target := &target.InMemoryTarget{Files: files}
sources := []source.ByteSource{ sources := []source.ByteSource{
{"sect/doc1.md", PAGE_WITH_WEIGHTED_INDEXES_1, "sect"}, {"sect/doc1.md", PAGE_WITH_WEIGHTED_INDEXES_1, "sect"},
{"sect/doc2.md", PAGE_WITH_WEIGHTED_INDEXES_2, "sect"}, {"sect/doc2.md", PAGE_WITH_WEIGHTED_INDEXES_2, "sect"},
{"sect/doc3.md", PAGE_WITH_WEIGHTED_INDEXES_3, "sect"}, {"sect/doc3.md", PAGE_WITH_WEIGHTED_INDEXES_3, "sect"},
} }
indexes := make(map[string]string) indexes := make(map[string]string)
indexes["tag"] = "tags" indexes["tag"] = "tags"
indexes["category"] = "categories" indexes["category"] = "categories"
s := &Site{ s := &Site{
Target: target, Target: target,
Config: Config{BaseUrl: "http://auth/bub/", Indexes: indexes}, Config: Config{BaseUrl: "http://auth/bub/", Indexes: indexes},
Source: &source.InMemorySource{sources}, Source: &source.InMemorySource{sources},
} }
s.initializeSiteInfo() s.initializeSiteInfo()
if err := s.CreatePages(); err != nil { if err := s.CreatePages(); err != nil {
t.Fatalf("Unable to create pages: %s", err) t.Fatalf("Unable to create pages: %s", err)
} }
if err := s.BuildSiteMeta(); err != nil { if err := s.BuildSiteMeta(); err != nil {
t.Fatalf("Unable to build site metadata: %s", err) t.Fatalf("Unable to build site metadata: %s", err)
} }
if s.Indexes["tags"]["a"][0].Page.Title != "foo" { if s.Indexes["tags"]["a"][0].Page.Title != "foo" {
t.Errorf("Pages in unexpected order, 'foo' expected first, got '%v'", s.Indexes["tags"]["a"][0].Page.Title) t.Errorf("Pages in unexpected order, 'foo' expected first, got '%v'", s.Indexes["tags"]["a"][0].Page.Title)
} }
if s.Indexes["categories"]["d"][0].Page.Title != "bar" { if s.Indexes["categories"]["d"][0].Page.Title != "bar" {
t.Errorf("Pages in unexpected order, 'bar' expected first, got '%v'", s.Indexes["categories"]["d"][0].Page.Title) t.Errorf("Pages in unexpected order, 'bar' expected first, got '%v'", s.Indexes["categories"]["d"][0].Page.Title)
} }
if s.Indexes["categories"]["e"][0].Page.Title != "bza" { if s.Indexes["categories"]["e"][0].Page.Title != "bza" {
t.Errorf("Pages in unexpected order, 'bza' expected first, got '%v'", s.Indexes["categories"]["e"][0].Page.Title) t.Errorf("Pages in unexpected order, 'bza' expected first, got '%v'", s.Indexes["categories"]["e"][0].Page.Title)
} }
} }

View File

@ -14,16 +14,16 @@
package bundle package bundle
type Tmpl struct { type Tmpl struct {
Name string Name string
Data string Data string
} }
func (t *GoHtmlTemplate) EmbedShortcodes() { func (t *GoHtmlTemplate) EmbedShortcodes() {
const k = "shortcodes" const k = "shortcodes"
t.AddInternalTemplate(k, "highlight.html", `{{ $lang := index .Params 0 }}{{ highlight .Inner $lang }}`) t.AddInternalTemplate(k, "highlight.html", `{{ $lang := index .Params 0 }}{{ highlight .Inner $lang }}`)
t.AddInternalTemplate(k, "test.html", `This is a simple Test`) t.AddInternalTemplate(k, "test.html", `This is a simple Test`)
t.AddInternalTemplate(k, "figure.html", `<!-- image --> t.AddInternalTemplate(k, "figure.html", `<!-- image -->
<figure {{ if isset .Params "class" }}class="{{ index .Params "class" }}"{{ end }}> <figure {{ if isset .Params "class" }}class="{{ index .Params "class" }}"{{ end }}>
{{ if isset .Params "link"}}<a href="{{ index .Params "link"}}">{{ end }} {{ if isset .Params "link"}}<a href="{{ index .Params "link"}}">{{ end }}
<img src="{{ index .Params "src" }}" {{ if or (isset .Params "alt") (isset .Params "caption") }}alt="{{ if isset .Params "alt"}}{{ index .Params "alt"}}{{else}}{{ index .Params "caption" }}{{ end }}"{{ end }} /> <img src="{{ index .Params "src" }}" {{ if or (isset .Params "alt") (isset .Params "caption") }}alt="{{ if isset .Params "alt"}}{{ index .Params "alt"}}{{else}}{{ index .Params "caption" }}{{ end }}"{{ end }} />

View File

@ -1,254 +1,254 @@
package bundle package bundle
import ( import (
"errors" "errors"
"github.com/eknkc/amber" "github.com/eknkc/amber"
"github.com/spf13/hugo/helpers" "github.com/spf13/hugo/helpers"
"html" "html"
"html/template" "html/template"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
) )
func Gt(a interface{}, b interface{}) bool { func Gt(a interface{}, b interface{}) bool {
var left, right int64 var left, right int64
av := reflect.ValueOf(a) av := reflect.ValueOf(a)
switch av.Kind() { switch av.Kind() {
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
left = int64(av.Len()) left = int64(av.Len())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
left = av.Int() left = av.Int()
case reflect.String: case reflect.String:
left, _ = strconv.ParseInt(av.String(), 10, 64) left, _ = strconv.ParseInt(av.String(), 10, 64)
} }
bv := reflect.ValueOf(b) bv := reflect.ValueOf(b)
switch bv.Kind() { switch bv.Kind() {
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
right = int64(bv.Len()) right = int64(bv.Len())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
right = bv.Int() right = bv.Int()
case reflect.String: case reflect.String:
right, _ = strconv.ParseInt(bv.String(), 10, 64) right, _ = strconv.ParseInt(bv.String(), 10, 64)
} }
return left > right return left > right
} }
// First is exposed to templates, to iterate over the first N items in a // First is exposed to templates, to iterate over the first N items in a
// rangeable list. // rangeable list.
func First(limit int, seq interface{}) (interface{}, error) { func First(limit int, seq interface{}) (interface{}, error) {
if limit < 1 { if limit < 1 {
return nil, errors.New("can't return negative/empty count of items from sequence") return nil, errors.New("can't return negative/empty count of items from sequence")
} }
seqv := reflect.ValueOf(seq) seqv := reflect.ValueOf(seq)
// this is better than my first pass; ripped from text/template/exec.go indirect(): // this is better than my first pass; ripped from text/template/exec.go indirect():
for ; seqv.Kind() == reflect.Ptr || seqv.Kind() == reflect.Interface; seqv = seqv.Elem() { for ; seqv.Kind() == reflect.Ptr || seqv.Kind() == reflect.Interface; seqv = seqv.Elem() {
if seqv.IsNil() { if seqv.IsNil() {
return nil, errors.New("can't iterate over a nil value") return nil, errors.New("can't iterate over a nil value")
} }
if seqv.Kind() == reflect.Interface && seqv.NumMethod() > 0 { if seqv.Kind() == reflect.Interface && seqv.NumMethod() > 0 {
break break
} }
} }
switch seqv.Kind() { switch seqv.Kind() {
case reflect.Array, reflect.Slice, reflect.String: case reflect.Array, reflect.Slice, reflect.String:
// okay // okay
default: default:
return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String()) return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
} }
if limit > seqv.Len() { if limit > seqv.Len() {
limit = seqv.Len() limit = seqv.Len()
} }
return seqv.Slice(0, limit).Interface(), nil return seqv.Slice(0, limit).Interface(), nil
} }
func IsSet(a interface{}, key interface{}) bool { func IsSet(a interface{}, key interface{}) bool {
av := reflect.ValueOf(a) av := reflect.ValueOf(a)
kv := reflect.ValueOf(key) kv := reflect.ValueOf(key)
switch av.Kind() { switch av.Kind() {
case reflect.Array, reflect.Chan, reflect.Slice: case reflect.Array, reflect.Chan, reflect.Slice:
if int64(av.Len()) > kv.Int() { if int64(av.Len()) > kv.Int() {
return true return true
} }
case reflect.Map: case reflect.Map:
if kv.Type() == av.Type().Key() { if kv.Type() == av.Type().Key() {
return av.MapIndex(kv).IsValid() return av.MapIndex(kv).IsValid()
} }
} }
return false return false
} }
func ReturnWhenSet(a interface{}, index int) interface{} { func ReturnWhenSet(a interface{}, index int) interface{} {
av := reflect.ValueOf(a) av := reflect.ValueOf(a)
switch av.Kind() { switch av.Kind() {
case reflect.Array, reflect.Slice: case reflect.Array, reflect.Slice:
if av.Len() > index { if av.Len() > index {
avv := av.Index(index) avv := av.Index(index)
switch avv.Kind() { switch avv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return avv.Int() return avv.Int()
case reflect.String: case reflect.String:
return avv.String() return avv.String()
} }
} }
} }
return "" return ""
} }
func Highlight(in interface{}, lang string) template.HTML { func Highlight(in interface{}, lang string) template.HTML {
var str string var str string
av := reflect.ValueOf(in) av := reflect.ValueOf(in)
switch av.Kind() { switch av.Kind() {
case reflect.String: case reflect.String:
str = av.String() str = av.String()
} }
if strings.HasPrefix(strings.TrimSpace(str), "<pre><code>") { if strings.HasPrefix(strings.TrimSpace(str), "<pre><code>") {
str = str[strings.Index(str, "<pre><code>")+11:] str = str[strings.Index(str, "<pre><code>")+11:]
} }
if strings.HasSuffix(strings.TrimSpace(str), "</code></pre>") { if strings.HasSuffix(strings.TrimSpace(str), "</code></pre>") {
str = str[:strings.LastIndex(str, "</code></pre>")] str = str[:strings.LastIndex(str, "</code></pre>")]
} }
return template.HTML(helpers.Highlight(html.UnescapeString(str), lang)) return template.HTML(helpers.Highlight(html.UnescapeString(str), lang))
} }
func SafeHtml(text string) template.HTML { func SafeHtml(text string) template.HTML {
return template.HTML(text) return template.HTML(text)
} }
type Template interface { type Template interface {
ExecuteTemplate(wr io.Writer, name string, data interface{}) error ExecuteTemplate(wr io.Writer, name string, data interface{}) error
Lookup(name string) *template.Template Lookup(name string) *template.Template
Templates() []*template.Template Templates() []*template.Template
New(name string) *template.Template New(name string) *template.Template
LoadTemplates(absPath string) LoadTemplates(absPath string)
AddTemplate(name, tpl string) error AddTemplate(name, tpl string) error
} }
type templateErr struct { type templateErr struct {
name string name string
err error err error
} }
type GoHtmlTemplate struct { type GoHtmlTemplate struct {
template.Template template.Template
errors []*templateErr errors []*templateErr
} }
func NewTemplate() Template { func NewTemplate() Template {
var templates = &GoHtmlTemplate{ var templates = &GoHtmlTemplate{
Template: *template.New(""), Template: *template.New(""),
errors: make([]*templateErr, 0), errors: make([]*templateErr, 0),
} }
funcMap := template.FuncMap{ funcMap := template.FuncMap{
"urlize": helpers.Urlize, "urlize": helpers.Urlize,
"gt": Gt, "gt": Gt,
"isset": IsSet, "isset": IsSet,
"echoParam": ReturnWhenSet, "echoParam": ReturnWhenSet,
"safeHtml": SafeHtml, "safeHtml": SafeHtml,
"first": First, "first": First,
"highlight": Highlight, "highlight": Highlight,
"add": func(a, b int) int { return a + b }, "add": func(a, b int) int { return a + b },
"sub": func(a, b int) int { return a - b }, "sub": func(a, b int) int { return a - b },
"div": func(a, b int) int { return a / b }, "div": func(a, b int) int { return a / b },
"mod": func(a, b int) int { return a % b }, "mod": func(a, b int) int { return a % b },
"mul": func(a, b int) int { return a * b }, "mul": func(a, b int) int { return a * b },
"modBool": func(a, b int) bool { return a%b == 0 }, "modBool": func(a, b int) bool { return a%b == 0 },
"lower": func(a string) string { return strings.ToLower(a) }, "lower": func(a string) string { return strings.ToLower(a) },
"upper": func(a string) string { return strings.ToUpper(a) }, "upper": func(a string) string { return strings.ToUpper(a) },
"title": func(a string) string { return strings.Title(a) }, "title": func(a string) string { return strings.Title(a) },
} }
templates.Funcs(funcMap) templates.Funcs(funcMap)
templates.LoadEmbedded() templates.LoadEmbedded()
return templates return templates
} }
func (t *GoHtmlTemplate) LoadEmbedded() { func (t *GoHtmlTemplate) LoadEmbedded() {
t.EmbedShortcodes() t.EmbedShortcodes()
} }
func (t *GoHtmlTemplate) AddInternalTemplate(prefix, name, tpl string) error { func (t *GoHtmlTemplate) AddInternalTemplate(prefix, name, tpl string) error {
return t.AddTemplate("_internal/"+prefix+"/"+name, tpl) return t.AddTemplate("_internal/"+prefix+"/"+name, tpl)
} }
func (t *GoHtmlTemplate) AddTemplate(name, tpl string) error { func (t *GoHtmlTemplate) AddTemplate(name, tpl string) error {
_, err := t.New(name).Parse(tpl) _, err := t.New(name).Parse(tpl)
if err != nil { if err != nil {
t.errors = append(t.errors, &templateErr{name: name, err: err}) t.errors = append(t.errors, &templateErr{name: name, err: err})
} }
return err return err
} }
func (t *GoHtmlTemplate) AddTemplateFile(name, path string) error { func (t *GoHtmlTemplate) AddTemplateFile(name, path string) error {
b, err := ioutil.ReadFile(path) b, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return err return err
} }
s := string(b) s := string(b)
_, err = t.New(name).Parse(s) _, err = t.New(name).Parse(s)
if err != nil { if err != nil {
t.errors = append(t.errors, &templateErr{name: name, err: err}) t.errors = append(t.errors, &templateErr{name: name, err: err})
} }
return err return err
} }
func (t *GoHtmlTemplate) generateTemplateNameFrom(base, path string) string { func (t *GoHtmlTemplate) generateTemplateNameFrom(base, path string) string {
return filepath.ToSlash(path[len(base)+1:]) return filepath.ToSlash(path[len(base)+1:])
} }
func ignoreDotFile(path string) bool { func ignoreDotFile(path string) bool {
return filepath.Base(path)[0] == '.' return filepath.Base(path)[0] == '.'
} }
func (t *GoHtmlTemplate) LoadTemplates(absPath string) { func (t *GoHtmlTemplate) LoadTemplates(absPath string) {
walker := func(path string, fi os.FileInfo, err error) error { walker := func(path string, fi os.FileInfo, err error) error {
if err != nil { if err != nil {
return nil return nil
} }
if !fi.IsDir() { if !fi.IsDir() {
if ignoreDotFile(path) { if ignoreDotFile(path) {
return nil return nil
} }
tplName := t.generateTemplateNameFrom(absPath, path) tplName := t.generateTemplateNameFrom(absPath, path)
if strings.HasSuffix(path, ".amber") { if strings.HasSuffix(path, ".amber") {
compiler := amber.New() compiler := amber.New()
// Parse the input file // Parse the input file
if err := compiler.ParseFile(path); err != nil { if err := compiler.ParseFile(path); err != nil {
return nil return nil
} }
// note t.New(tplName) // note t.New(tplName)
if _, err := compiler.CompileWithTemplate(t.New(tplName)); err != nil { if _, err := compiler.CompileWithTemplate(t.New(tplName)); err != nil {
return err return err
} }
} else { } else {
t.AddTemplateFile(tplName, path) t.AddTemplateFile(tplName, path)
} }
} }
return nil return nil
} }
filepath.Walk(absPath, walker) filepath.Walk(absPath, walker)
} }

View File

@ -1,56 +1,56 @@
package watcher package watcher
import ( import (
"github.com/howeyc/fsnotify" "github.com/howeyc/fsnotify"
"time" "time"
) )
type Batcher struct { type Batcher struct {
*fsnotify.Watcher *fsnotify.Watcher
interval time.Duration interval time.Duration
done chan struct{} done chan struct{}
Event chan []*fsnotify.FileEvent // Events are returned on this channel Event chan []*fsnotify.FileEvent // Events are returned on this channel
} }
func New(interval time.Duration) (*Batcher, error) { func New(interval time.Duration) (*Batcher, error) {
watcher, err := fsnotify.NewWatcher() watcher, err := fsnotify.NewWatcher()
batcher := &Batcher{} batcher := &Batcher{}
batcher.Watcher = watcher batcher.Watcher = watcher
batcher.interval = interval batcher.interval = interval
batcher.done = make(chan struct{}, 1) batcher.done = make(chan struct{}, 1)
batcher.Event = make(chan []*fsnotify.FileEvent, 1) batcher.Event = make(chan []*fsnotify.FileEvent, 1)
if err == nil { if err == nil {
go batcher.run() go batcher.run()
} }
return batcher, err return batcher, err
} }
func (b *Batcher) run() { func (b *Batcher) run() {
tick := time.Tick(b.interval) tick := time.Tick(b.interval)
evs := make([]*fsnotify.FileEvent, 0) evs := make([]*fsnotify.FileEvent, 0)
OuterLoop: OuterLoop:
for { for {
select { select {
case ev := <-b.Watcher.Event: case ev := <-b.Watcher.Event:
evs = append(evs, ev) evs = append(evs, ev)
case <-tick: case <-tick:
if len(evs) == 0 { if len(evs) == 0 {
continue continue
} }
b.Event <- evs b.Event <- evs
evs = make([]*fsnotify.FileEvent, 0) evs = make([]*fsnotify.FileEvent, 0)
case <-b.done: case <-b.done:
break OuterLoop break OuterLoop
} }
} }
close(b.done) close(b.done)
} }
func (b *Batcher) Close() { func (b *Batcher) Close() {
b.done <- struct{}{} b.done <- struct{}{}
b.Watcher.Close() b.Watcher.Close()
} }