diff options
Diffstat (limited to 'hugolib/content_map.go')
-rw-r--r-- | hugolib/content_map.go | 161 |
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) }) |