summaryrefslogtreecommitdiffstats
path: root/hugolib/content_map.go
diff options
context:
space:
mode:
Diffstat (limited to 'hugolib/content_map.go')
-rw-r--r--hugolib/content_map.go161
1 files changed, 93 insertions, 68 deletions
diff --git a/hugolib/content_map.go b/hugolib/content_map.go
index 3c57fffcf..ddcc70707 100644
--- a/hugolib/content_map.go
+++ b/hugolib/content_map.go
@@ -20,6 +20,8 @@ import (
"strings"
"sync"
+ "github.com/gohugoio/hugo/helpers"
+
"github.com/gohugoio/hugo/resources/page"
"github.com/pkg/errors"
@@ -31,27 +33,26 @@ import (
)
// We store the branch nodes in either the `sections` or `taxonomies` tree
-// with their path as a key; Unix style slashes, a leading slash but no
-// trailing slash.
+// with their path as a key; Unix style slashes, a leading and trailing slash.
//
-// E.g. "/blog" or "/categories/funny"
+// E.g. "/blog/" or "/categories/funny/"
//
// Pages that belongs to a section are stored in the `pages` tree below
-// the section name and a branch separator, e.g. "/blog__hb_". A page is
+// the section name and a branch separator, e.g. "/blog/__hb_". A page is
// given a key using the path below the section and the base filename with no extension
// with a leaf separator added.
//
// For bundled pages (/mybundle/index.md), we use the folder name.
//
-// An exmple of a full page key would be "/blog__hb_/page1__hl_"
+// An exmple of a full page key would be "/blog/__hb_page1__hl_"
//
// Bundled resources are stored in the `resources` having their path prefixed
// with the bundle they belong to, e.g.
-// "/blog__hb_/bundle__hl_data.json".
+// "/blog/__hb_bundle__hl_data.json".
//
// The weighted taxonomy entries extracted from page front matter are stored in
// the `taxonomyEntries` tree below /plural/term/page-key, e.g.
-// "/categories/funny/blog__hb_/bundle__hl_".
+// "/categories/funny/blog/__hb_bundle__hl_".
const (
cmBranchSeparator = "__hb_"
cmLeafSeparator = "__hl_"
@@ -105,7 +106,7 @@ func newContentMap(cfg contentMapConfig) *contentMap {
addToReverseMap(mountKey, n, m)
}
}
- k := strings.TrimSuffix(path.Base(s), cmLeafSeparator)
+ k := strings.TrimPrefix(strings.TrimSuffix(path.Base(s), cmLeafSeparator), cmBranchSeparator)
addToReverseMap(k, n, m)
return false
})
@@ -127,18 +128,15 @@ type cmInsertKeyBuilder struct {
}
func (b cmInsertKeyBuilder) ForPage(s string) *cmInsertKeyBuilder {
- // TODO2 fmt.Println("ForPage:", s, "baseKey:", b.baseKey, "key:", b.key)
+ //fmt.Println("ForPage:", s, "baseKey:", b.baseKey, "key:", b.key)
baseKey := b.baseKey
b.baseKey = s
- if !strings.HasPrefix(s, "/") {
- s = "/" + s
- }
-
if baseKey != "/" {
// Don't repeat the section path in the key.
s = strings.TrimPrefix(s, baseKey)
}
+ s = strings.TrimPrefix(s, "/")
switch b.tree {
case b.m.sections:
@@ -154,10 +152,10 @@ func (b cmInsertKeyBuilder) ForPage(s string) *cmInsertKeyBuilder {
}
func (b cmInsertKeyBuilder) ForResource(s string) *cmInsertKeyBuilder {
- // TODO2 fmt.Println("ForResource:", s, "baseKey:", b.baseKey, "key:", b.key)
+ //fmt.Println("ForResource:", s, "baseKey:", b.baseKey, "key:", b.key)
- s = strings.TrimPrefix(s, "/")
- s = strings.TrimPrefix(s, strings.TrimPrefix(b.baseKey, "/")+"/")
+ baseKey := helpers.AddTrailingSlash(b.baseKey)
+ s = strings.TrimPrefix(s, baseKey)
switch b.tree {
case b.m.pages:
@@ -173,14 +171,23 @@ func (b cmInsertKeyBuilder) ForResource(s string) *cmInsertKeyBuilder {
func (b *cmInsertKeyBuilder) Insert(n *contentNode) *cmInsertKeyBuilder {
if b.err == nil {
- b.tree.Insert(cleanTreeKey(b.key), n)
+ b.tree.Insert(b.Key(), n)
}
return b
}
+func (b *cmInsertKeyBuilder) Key() string {
+ switch b.tree {
+ case b.m.sections, b.m.taxonomies:
+ return cleanSectionTreeKey(b.key)
+ default:
+ return cleanTreeKey(b.key)
+ }
+}
+
func (b *cmInsertKeyBuilder) DeleteAll() *cmInsertKeyBuilder {
if b.err == nil {
- b.tree.DeletePrefix(cleanTreeKey(b.key))
+ b.tree.DeletePrefix(b.Key())
}
return b
}
@@ -211,15 +218,16 @@ func (b *cmInsertKeyBuilder) WithFile(fi hugofs.FileMetaInfo) *cmInsertKeyBuilde
}
func (b *cmInsertKeyBuilder) WithSection(s string) *cmInsertKeyBuilder {
+ s = cleanSectionTreeKey(s)
b.newTopLevel()
b.tree = b.m.sections
b.baseKey = s
b.key = s
- // TODO2 fmt.Println("WithSection:", s, "baseKey:", b.baseKey, "key:", b.key)
return b
}
func (b *cmInsertKeyBuilder) WithTaxonomy(s string) *cmInsertKeyBuilder {
+ s = cleanSectionTreeKey(s)
b.newTopLevel()
b.tree = b.m.taxonomies
b.baseKey = s
@@ -233,20 +241,17 @@ func (b *cmInsertKeyBuilder) getBundle(s string) (string, string) {
m := b.m
section, _ := m.getSection(s)
- p := s
- if section != "/" {
- p = strings.TrimPrefix(s, section)
- }
+ p := strings.TrimPrefix(s, section)
- bundlePathParts := strings.Split(p, "/")[1:]
+ bundlePathParts := strings.Split(p, "/")
basePath := section + cmBranchSeparator
// Put it into an existing bundle if found.
for i := len(bundlePathParts) - 2; i >= 0; i-- {
bundlePath := path.Join(bundlePathParts[:i]...)
- searchKey := basePath + "/" + bundlePath + cmLeafSeparator
+ searchKey := basePath + bundlePath + cmLeafSeparator
if _, found := m.pages.Get(searchKey); found {
- return section + "/" + bundlePath, searchKey
+ return section + bundlePath, searchKey
}
}
@@ -432,7 +437,7 @@ func (m *contentMap) CreateMissingNodes() error {
sectionPath = sectionPath[:firstSlash]
}
}
- sect = cleanTreeKey(sect)
+ sect = cleanSectionTreeKey(sect)
_, found := m.sections.Get(sect)
if !found {
m.sections.Insert(sect, &contentNode{path: sectionPath})
@@ -440,7 +445,7 @@ func (m *contentMap) CreateMissingNodes() error {
}
for _, view := range m.cfg.taxonomyConfig {
- s := cleanTreeKey(view.plural)
+ s := cleanSectionTreeKey(view.plural)
_, found := m.taxonomies.Get(s)
if !found {
b := &contentNode{
@@ -476,15 +481,20 @@ func (m *contentMap) newContentNodeFromFi(fi hugofs.FileMetaInfo) *contentNode {
}
func (m *contentMap) getFirstSection(s string) (string, *contentNode) {
+ s = helpers.AddTrailingSlash(s)
for {
k, v, found := m.sections.LongestPrefix(s)
+
if !found {
return "", nil
}
- if strings.Count(k, "/") == 1 {
+
+ if strings.Count(k, "/") <= 2 {
return k, v.(*contentNode)
}
- s = path.Dir(s)
+
+ s = helpers.AddTrailingSlash(path.Dir(strings.TrimSuffix(s, "/")))
+
}
}
@@ -507,10 +517,7 @@ func (m *contentMap) getOrCreateSection(n *contentNode, s string) (string, *cont
}
if mustCreate {
- k = s[:strings.Index(s[1:], "/")+1]
- if k == "" {
- k = "/"
- }
+ k = cleanSectionTreeKey(s[:strings.Index(s[1:], "/")+1])
b = &contentNode{
path: n.rootSection(),
@@ -523,7 +530,9 @@ func (m *contentMap) getOrCreateSection(n *contentNode, s string) (string, *cont
}
func (m *contentMap) getPage(section, name string) *contentNode {
- key := section + cmBranchSeparator + "/" + name + cmLeafSeparator
+ section = helpers.AddTrailingSlash(section)
+ key := section + cmBranchSeparator + name + cmLeafSeparator
+
v, found := m.pages.Get(key)
if found {
return v.(*contentNode)
@@ -532,7 +541,9 @@ func (m *contentMap) getPage(section, name string) *contentNode {
}
func (m *contentMap) getSection(s string) (string, *contentNode) {
- k, v, found := m.sections.LongestPrefix(path.Dir(s))
+ s = helpers.AddTrailingSlash(path.Dir(strings.TrimSuffix(s, "/")))
+
+ k, v, found := m.sections.LongestPrefix(s)
if found {
return k, v.(*contentNode)
@@ -541,21 +552,18 @@ func (m *contentMap) getSection(s string) (string, *contentNode) {
}
func (m *contentMap) getTaxonomyParent(s string) (string, *contentNode) {
- s = path.Dir(s)
- if s == "/" {
- v, found := m.sections.Get(s)
- if found {
- return s, v.(*contentNode)
- }
- return "", nil
+ s = helpers.AddTrailingSlash(path.Dir(strings.TrimSuffix(s, "/")))
+ k, v, found := m.taxonomies.LongestPrefix(s)
+
+ if found {
+ return k, v.(*contentNode)
}
- for _, tree := range []*contentTree{m.taxonomies, m.sections} {
- k, v, found := tree.LongestPrefix(s)
- if found {
- return k, v.(*contentNode)
- }
+ v, found = m.sections.Get("/")
+ if found {
+ return s, v.(*contentNode)
}
+
return "", nil
}
@@ -569,6 +577,15 @@ func cleanTreeKey(k string) string {
return k
}
+func cleanSectionTreeKey(k string) string {
+ k = cleanTreeKey(k)
+ if k != "/" {
+ k += "/"
+ }
+
+ return k
+}
+
func (m *contentMap) onSameLevel(s1, s2 string) bool {
return strings.Count(s1, "/") == strings.Count(s2, "/")
}
@@ -606,13 +623,13 @@ func (m *contentMap) deleteOrphanSections() {
return false
}
- if s == "/" || strings.Count(s, "/") > 1 {
+ if s == "/" || strings.Count(s, "/") > 2 {
return false
}
prefixBundle := s + cmBranchSeparator
- if !(m.sections.hasPrefix(s+"/") || m.pages.hasPrefix(prefixBundle) || m.resources.hasPrefix(prefixBundle)) {
+ if !(m.sections.hasBelow(s) || m.pages.hasBelow(prefixBundle) || m.resources.hasBelow(prefixBundle)) {
sectionsToDelete = append(sectionsToDelete, s)
}
@@ -630,13 +647,15 @@ func (m *contentMap) deletePage(s string) {
}
func (m *contentMap) deleteSectionByPath(s string) {
- m.sections.Delete(s)
- m.sections.DeletePrefix(s + "/")
- m.pages.DeletePrefix(s + cmBranchSeparator)
- m.pages.DeletePrefix(s + "/")
- m.resources.DeletePrefix(s + cmBranchSeparator)
- m.resources.DeletePrefix(s + cmLeafSeparator)
- m.resources.DeletePrefix(s + "/")
+ if !strings.HasSuffix(s, "/") {
+ panic("section must end with a slash")
+ }
+ if !strings.HasPrefix(s, "/") {
+ panic("section must start with a slash")
+ }
+ m.sections.DeletePrefix(s)
+ m.pages.DeletePrefix(s)
+ m.resources.DeletePrefix(s)
}
func (m *contentMap) deletePageByPath(s string) {
@@ -648,8 +667,7 @@ func (m *contentMap) deletePageByPath(s string) {
}
func (m *contentMap) deleteTaxonomy(s string) {
- m.taxonomies.Delete(s)
- m.taxonomies.DeletePrefix(s + "/")
+ m.taxonomies.DeletePrefix(s)
}
func (m *contentMap) reduceKeyPart(dir, filename string) string {
@@ -817,7 +835,7 @@ func (c *contentTree) WalkQuery(query pageMapQuery, walkFn contentTreeNodeCallba
filter = contentTreeNoListAlwaysFilter
}
if query.Prefix != "" {
- c.WalkPrefix(query.Prefix, func(s string, v interface{}) bool {
+ c.WalkBelow(query.Prefix, func(s string, v interface{}) bool {
n := v.(*contentNode)
if filter != nil && filter(s, n) {
return false
@@ -862,6 +880,18 @@ func (c contentTrees) WalkPrefix(prefix string, fn contentTreeNodeCallback) {
}
}
+// WalkBelow walks the tree below the given prefix, i.e. it skips the
+// node with the given prefix as key.
+func (c *contentTree) WalkBelow(prefix string, fn radix.WalkFn) {
+ c.Tree.WalkPrefix(prefix, func(s string, v interface{}) bool {
+ if s == prefix {
+ return false
+ }
+ return fn(s, v)
+ })
+
+}
+
func (c *contentTree) getMatch(matches func(b *contentNode) bool) string {
var match string
c.Walk(func(s string, v interface{}) bool {
@@ -881,9 +911,9 @@ func (c *contentTree) getMatch(matches func(b *contentNode) bool) string {
return match
}
-func (c *contentTree) hasPrefix(s string) bool {
+func (c *contentTree) hasBelow(s1 string) bool {
var t bool
- c.Tree.WalkPrefix(s, func(s string, v interface{}) bool {
+ c.WalkBelow(s1, func(s2 string, v interface{}) bool {
t = true
return true
})
@@ -953,12 +983,7 @@ func (c *contentTreeRef) getPagesRecursive() page.Pages {
Filter: c.n.p.m.getListFilter(true),
}
- query.Prefix = c.key + cmBranchSeparator
- c.m.collectPages(query, func(c *contentNode) {
- pas = append(pas, c.p)
- })
-
- query.Prefix = c.key + "/"
+ query.Prefix = c.key
c.m.collectPages(query, func(c *contentNode) {
pas = append(pas, c.p)
})