ref: d8e1834910d2b845ee5066571a61be49a7a1451c
parent: a82efe5bb131f1d4a811d3220c2ce40d56aa9eaf
author: Noah Campbell <noahcampbell@gmail.com>
date: Wed Sep 18 05:15:46 EDT 2013
Fix parsing edge case of frontmatter When the frontmatter contains a - (or other delimiter) close to the closing frontmatter delimiter, frontmatter detection would fail.
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -19,10 +19,10 @@
"errors"
"fmt"
"github.com/BurntSushi/toml"
+ "github.com/spf13/hugo/parser"
helper "github.com/spf13/hugo/template"
"github.com/spf13/hugo/template/bundle"
"github.com/theplant/blackfriday"
- "github.com/spf13/hugo/parser"
"html/template"
"io"
"launchpad.net/goyaml"
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -10,14 +10,9 @@
var EMPTY_PAGE = ""
-var SIMPLE_PAGE = `---
-title: Simple
----
-Simple Page
-`
+var SIMPLE_PAGE = "---\ntitle: Simple\n---\nSimple Page\n"
+var INVALID_FRONT_MATTER_MISSING = "This is a test"
-var INVALID_FRONT_MATTER_MISSING = `This is a test`
-
var INVALID_FRONT_MATTER_SHORT_DELIM = `
--
title: Short delim start
@@ -95,7 +90,7 @@
var SIMPLE_PAGE_WITH_SUMMARY_DELIMITER = `---
title: Simple
---
-Simple Page
+Summary Next Line
<!--more-->
Some more text
@@ -104,7 +99,7 @@
var SIMPLE_PAGE_WITH_SUMMARY_DELIMITER_SAME_LINE = `---
title: Simple
---
-Simple Page<!--more-->
+Summary Same Line<!--more-->
Some more text
`
@@ -144,7 +139,7 @@
func checkPageContent(t *testing.T, page *Page, content string) { if page.Content != template.HTML(content) {- t.Fatalf("Page content is: %s. Expected %s", page.Content, content)+ t.Fatalf("Page content mismatch\nexp: %q\ngot: %q", content, page.Content)}
}
@@ -190,8 +185,8 @@
t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)}
checkPageTitle(t, p, "Simple")
- checkPageContent(t, p, "<p>Simple Page</p>\n\n<p>Some more text</p>\n")
- checkPageSummary(t, p, "<p>Simple Page</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")
checkPageType(t, p, "page")
checkPageLayout(t, p, "page/single.html")
@@ -203,8 +198,8 @@
t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)}
checkPageTitle(t, p, "Simple")
- checkPageContent(t, p, "<p>Simple Page</p>\n\n<p>Some more text</p>\n")
- checkPageSummary(t, p, "<p>Simple Page</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")
checkPageType(t, p, "page")
checkPageLayout(t, p, "page/single.html")
}
@@ -243,7 +238,7 @@
err string
}{ {INVALID_FRONT_MATTER_SHORT_DELIM, "Unable to locate frontmatter"},- {INVALID_FRONT_MATTER_SHORT_DELIM_ENDING, "EOF"},+ {INVALID_FRONT_MATTER_SHORT_DELIM_ENDING, "Unable to read frontmatter at filepos 45: EOF"}, {INVALID_FRONT_MATTER_MISSING, "Unable to locate frontmatter"},}
for _, test := range tests {--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -14,7 +14,6 @@
package hugolib
import (
- "io"
"bitbucket.org/pkg/inflect"
"bytes"
"fmt"
@@ -25,6 +24,7 @@
"github.com/spf13/hugo/transform"
"github.com/spf13/nitro"
"html/template"
+ "io"
"os"
"path"
"strings"
@@ -68,18 +68,18 @@
//
// 5. The entire collection of files is written to disk.
type Site struct {- Config Config
- Pages Pages
- Tmpl bundle.Template
- Indexes IndexList
- Source source.Input
- Sections Index
- Info SiteInfo
- Shortcodes map[string]ShortcodeFunc
- timer *nitro.B
+ Config Config
+ Pages Pages
+ Tmpl bundle.Template
+ Indexes IndexList
+ Source source.Input
+ Sections Index
+ Info SiteInfo
+ Shortcodes map[string]ShortcodeFunc
+ timer *nitro.B
Transformer *transform.Transformer
- Target target.Output
- Alias target.AliasPublisher
+ Target target.Output
+ Alias target.AliasPublisher
}
type SiteInfo struct {--- a/hugolib/site_url_test.go
+++ b/hugolib/site_url_test.go
@@ -10,7 +10,12 @@
const SLUG_DOC_1 = "---\ntitle: slug doc 1\nslug: slug-doc-1\naliases:\n - sd1/foo/\n - sd2\n - sd3/\n - sd4.html\n---\nslug doc 1 content\n"
-const SLUG_DOC_2 = "---\ntitle: slug doc 2\nslug: slug-doc-2\n---\nslug doc 2 content\n"
+const SLUG_DOC_2 = `---
+title: slug doc 2
+slug: slug-doc-2
+---
+slug doc 2 content
+`
const INDEX_TEMPLATE = "{{ range .Data.Pages }}.{{ end }}"@@ -58,7 +63,7 @@
var urlFakeSource = []byteSource{ {"content/blue/doc1.md", []byte(SLUG_DOC_1)},-// {"content/blue/doc2.md", []byte(SLUG_DOC_2)},+ {"content/blue/doc2.md", []byte(SLUG_DOC_2)},}
func TestPageCount(t *testing.T) {@@ -95,7 +100,7 @@
t.Errorf("No indexed rendered. %v", target.files)}
- expected := "<html><head></head><body>.</body></html>"
+ expected := "<html><head></head><body>..</body></html>"
if string(blueIndex) != expected { t.Errorf("Index template does not match expected: %q, got: %q", expected, string(blueIndex))}
--- a/parser/page.go
+++ b/parser/page.go
@@ -2,9 +2,9 @@
import (
"bufio"
- "fmt"
"bytes"
"errors"
+ "fmt"
"io"
"unicode"
)
@@ -164,22 +164,34 @@
}
func extractFrontMatterDelims(r *bufio.Reader, left, right []byte) (fm FrontMatter, err error) {- var level int = 0
- var sameDelim = bytes.Equal(left, right)
+ var (
+ c byte
+ level int = 0
+ bytesRead int = 0
+ sameDelim = bytes.Equal(left, right)
+ )
+
wr := new(bytes.Buffer)
for {- c, err := r.ReadByte()
- if err != nil {- return nil, err
+ if c, err = r.ReadByte(); err != nil {+ return nil, fmt.Errorf("Unable to read frontmatter at filepos %d: %s", bytesRead, err)}
+ bytesRead += 1
switch c {case left[0]:
- match, err := matches(r, wr, []byte{c}, left)- if err != nil {+ var (
+ buf []byte = []byte{c}+ remaining []byte
+ )
+
+ if remaining, err = r.Peek(len(left) - 1); err != nil {return nil, err
}
- if match {+
+ buf = append(buf, remaining...)
+
+ if bytes.Equal(buf, left) { if sameDelim { if level == 0 {level = 1
@@ -190,6 +202,19 @@
level += 1
}
}
+
+ if _, err = wr.Write([]byte{c}); err != nil {+ return nil, err
+ }
+
+ if level == 0 {+ if _, err = r.Read(remaining); err != nil {+ return nil, err
+ }
+ if _, err = wr.Write(remaining); err != nil {+ return nil, err
+ }
+ }
case right[0]:
match, err := matches(r, wr, []byte{c}, right) if err != nil {@@ -216,6 +241,10 @@
return nil, errors.New("Could not find front matter.")}
+func matches_quick(buf, expected []byte) (ok bool, err error) {+ return bytes.Equal(expected, buf), nil
+}
+
func matches(r *bufio.Reader, wr io.Writer, c, expected []byte) (ok bool, err error) { if len(expected) == 1 { if _, err = wr.Write(c); err != nil {@@ -223,16 +252,13 @@
}
return bytes.Equal(c, expected), nil
}
+
buf := make([]byte, len(expected)-1)
- if _, err = r.Read(buf); err != nil {+ if buf, err = r.Peek(len(expected) - 1); err != nil {return
}
buf = append(c, buf...)
- if _, err = wr.Write(buf); err != nil {- return
- }
-
return bytes.Equal(expected, buf), nil
}
--- a/parser/parse_frontmatter_test.go
+++ b/parser/parse_frontmatter_test.go
@@ -24,12 +24,15 @@
CONTENT_INCOMPLETE_BEG_FM_DELIM = "--\ntitle: incomplete beg fm delim\n---\nincomplete frontmatter delim"
CONTENT_INCOMPLETE_END_FM_DELIM = "---\ntitle: incomplete end fm delim\n--\nincomplete frontmatter delim"
CONTENT_MISSING_END_FM_DELIM = "---\ntitle: incomplete end fm delim\nincomplete frontmatter delim"
+ CONTENT_SLUG_WORKING = "---\ntitle: slug doc 2\nslug: slug-doc-2\n\n---\nslug doc 2 content"
+ CONTENT_SLUG_WORKING_VARIATION = "---\ntitle: slug doc 3\nslug: slug-doc 3\n---\nslug doc 3 content"
+ CONTENT_SLUG_BUG = "---\ntitle: slug doc 2\nslug: slug-doc-2\n---\nslug doc 2 content"
CONTENT_FM_NO_DOC = "---\ntitle: no doc\n---"
- CONTENT_WITH_JS_FM = "{\n \"categories\": \"d\",\n \"tags\": [\n \"a\", \n \"b\", \n \"c\"\n ]\n}\nJSON Front Matter with tags and categories"+ CONTENT_WITH_JS_FM = "{\n \"categories\": \"d\",\n \"tags\": [\n \"a\", \n \"b\", \n \"c\"\n ]\n}\nJSON Front Matter with tags and categories")
var lineEndings = []string{"\n", "\r\n"}-var delimiters = []string{"-", "+"}+var delimiters = []string{"---", "+++"} func pageMust(p Page, err error) *page { if err != nil {@@ -83,13 +86,13 @@
return
}
if !bytes.Equal(p.frontmatter, []byte(frontMatter)) {- t.Errorf("expected frontmatter %q, got %q", frontMatter, p.frontmatter)+ t.Errorf("frontmatter mismatch\nexp: %q\ngot: %q", frontMatter, p.frontmatter)}
}
func checkPageContent(t *testing.T, p *page, expected string) { if !bytes.Equal(p.content, []byte(expected)) {- t.Errorf("expected content %q, got %q", expected, p.content)+ t.Errorf("content mismatch\nexp: %q\ngot: %q", expected, p.content)}
}
@@ -101,6 +104,7 @@
frontMatter string
bodycontent string
}{+
{CONTENT_NO_FRONTMATTER, true, true, "", "a page with no front matter"}, {CONTENT_WITH_FRONTMATTER, true, false, "---\ntitle: front matter\n---\n", "Content with front matter"}, {CONTENT_HTML_NODOCTYPE, false, true, "", "<html>\n\t<body>\n\t</body>\n</html>"},@@ -109,6 +113,9 @@
{CONTENT_LWS_HTML, false, true, "", "<html><body></body></html>"}, {CONTENT_LWS_LF_HTML, false, true, "", "<html><body></body></html>"}, {CONTENT_WITH_JS_FM, true, false, "{\n \"categories\": \"d\",\n \"tags\": [\n \"a\", \n \"b\", \n \"c\"\n ]\n}", "JSON Front Matter with tags and categories"},+ {CONTENT_SLUG_WORKING, true, false, "---\ntitle: slug doc 2\nslug: slug-doc-2\n\n---\n", "slug doc 2 content"},+ {CONTENT_SLUG_WORKING_VARIATION, true, false, "---\ntitle: slug doc 3\nslug: slug-doc 3\n---\n", "slug doc 3 content"},+ {CONTENT_SLUG_BUG, true, false, "---\ntitle: slug doc 2\nslug: slug-doc-2\n---\n", "slug doc 2 content"},}
for _, test := range tests {@@ -224,6 +231,7 @@
{"---\nralb\n---\n", []byte("---\nralb\n---\n"), true}, {"---\nminc\n---\ncontent", []byte("---\nminc\n---\n"), true}, {"---\ncnim\n---\ncontent\n", []byte("---\ncnim\n---\n"), true},+ {"---\ntitle: slug doc 2\nslug: slug-doc-2\n---\ncontent\n", []byte("---\ntitle: slug doc 2\nslug: slug-doc-2\n---\n"), true},}
for _, test := range tests {@@ -231,8 +239,8 @@
test.frontmatter = strings.Replace(test.frontmatter, "\n", ending, -1)
test.extracted = bytes.Replace(test.extracted, []byte("\n"), []byte(ending), -1) for _, delim := range delimiters {- test.frontmatter = strings.Replace(test.frontmatter, "-", delim, -1)
- test.extracted = bytes.Replace(test.extracted, []byte("-"), []byte(delim), -1)+ test.frontmatter = strings.Replace(test.frontmatter, "---", delim, -1)
+ test.extracted = bytes.Replace(test.extracted, []byte("---"), []byte(delim), -1)line, err := peekLine(bufio.NewReader(strings.NewReader(test.frontmatter)))
if err != nil {continue
@@ -245,8 +253,7 @@
continue
}
if !bytes.Equal(fm, test.extracted) {- t.Logf("\n%q\n", string(test.frontmatter))- t.Errorf("Expected front matter %q. got %q", string(test.extracted), fm)+ t.Errorf("Frontmatter did not match:\nexp: %q\ngot: %q", string(test.extracted), fm)}
}
}
@@ -285,8 +292,7 @@
}
if !bytes.Equal(fm, []byte(test.extracted)) { t.Logf("\n%q\n", string(test.frontmatter))- t.Errorf("Expected front matter %q. got %q", string(test.extracted), fm)+ t.Errorf("Frontmatter did not match:\nexp: %q\ngot: %q", string(test.extracted), fm)}
}
}
-
--- a/transform/post.go
+++ b/transform/post.go
@@ -1,9 +1,9 @@
package transform
import (
+ htmltran "code.google.com/p/go-html-transform/html/transform"
"io"
"net/url"
- htmltran "code.google.com/p/go-html-transform/html/transform"
)
type Transformer struct {--- a/transform/posttrans_test.go
+++ b/transform/posttrans_test.go
@@ -1,23 +1,23 @@
package transform
import (
- "testing"
- "strings"
"bytes"
+ "strings"
+ "testing"
)
const H5_JS_CONTENT_DOUBLE_QUOTE = "<!DOCTYPE html><html><head><script src=\"foobar.js\"></script></head><body><nav><h1>title</h1></nav><article>content <a href='/foobar'>foobar</a>. Follow up</article></body></html>"
const H5_JS_CONTENT_SINGLE_QUOTE = "<!DOCTYPE html><html><head><script src='foobar.js'></script></head><body><nav><h1>title</h1></nav><article>content <a href='/foobar'>foobar</a>. Follow up</article></body></html>"
const H5_JS_CONTENT_ABS_URL = "<!DOCTYPE html><html><head><script src=\"http://user@host:10234/foobar.js\"></script></head><body><nav><h1>title</h1></nav><article>content <a href=\"https://host/foobar\">foobar</a>. Follow up</article></body></html>"
+
// URL doesn't recognize authorities. BUG?
//const H5_JS_CONTENT_ABS_URL = "<!DOCTYPE html><html><head><script src=\"//host/foobar.js\"></script></head><body><nav><h1>title</h1></nav><article>content <a href=\"https://host/foobar\">foobar</a>. Follow up</article></body></html>"
const CORRECT_OUTPUT_SRC_HREF = "<!DOCTYPE html><html><head><script src=\"http://base/foobar.js\"></script></head><body><nav><h1>title</h1></nav><article>content <a href=\"http://base/foobar\">foobar</a>. Follow up</article></body></html>"
-
func TestAbsUrlify(t *testing.T) { tests := []struct {- content string
+ content string
expected string
}{ {H5_JS_CONTENT_DOUBLE_QUOTE, CORRECT_OUTPUT_SRC_HREF},@@ -29,13 +29,13 @@
tr := &Transformer{BaseURL: "http://base",
}
- out := new(bytes.Buffer)
- err := tr.Apply(strings.NewReader(test.content), out)
- if err != nil {- t.Errorf("Unexpected error: %s", err)+ out := new(bytes.Buffer)
+ err := tr.Apply(strings.NewReader(test.content), out)
+ if err != nil {+ t.Errorf("Unexpected error: %s", err)+ }
+ if test.expected != string(out.Bytes()) {+ t.Errorf("Expected:\n%s\nGot:\n%s", test.expected, string(out.Bytes()))+ }
}
- if test.expected != string(out.Bytes()) {- t.Errorf("Expected:\n%s\nGot:\n%s", test.expected, string(out.Bytes()))- }
-}
}
--
⑨