github.com/neohugo/neohugo@v0.123.8/hugolib/page__tree.go (about)

     1  // Copyright 2019 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package hugolib
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/neohugo/neohugo/common/paths"
    22  	"github.com/neohugo/neohugo/common/types"
    23  	"github.com/neohugo/neohugo/hugolib/doctree"
    24  	"github.com/neohugo/neohugo/resources/kinds"
    25  	"github.com/neohugo/neohugo/resources/page"
    26  )
    27  
    28  // pageTree holds the treen navigational method for a Page.
    29  type pageTree struct {
    30  	p *pageState
    31  }
    32  
    33  func (pt pageTree) IsAncestor(other any) bool {
    34  	n, ok := other.(contentNodeI)
    35  	if !ok {
    36  		return false
    37  	}
    38  
    39  	if n.Path() == pt.p.Path() {
    40  		return false
    41  	}
    42  
    43  	return strings.HasPrefix(n.Path(), paths.AddTrailingSlash(pt.p.Path()))
    44  }
    45  
    46  func (pt pageTree) IsDescendant(other any) bool {
    47  	n, ok := other.(contentNodeI)
    48  	if !ok {
    49  		return false
    50  	}
    51  
    52  	if n.Path() == pt.p.Path() {
    53  		return false
    54  	}
    55  
    56  	return strings.HasPrefix(pt.p.Path(), paths.AddTrailingSlash(n.Path()))
    57  }
    58  
    59  func (pt pageTree) CurrentSection() page.Page {
    60  	if kinds.IsBranch(pt.p.Kind()) {
    61  		return pt.p
    62  	}
    63  
    64  	dir := pt.p.m.pathInfo.Dir()
    65  	if dir == "/" {
    66  		return pt.p.s.home
    67  	}
    68  
    69  	_, n := pt.p.s.pageMap.treePages.LongestPrefix(dir, true, func(n contentNodeI) bool { return n.isContentNodeBranch() })
    70  	if n != nil {
    71  		return n.(page.Page)
    72  	}
    73  
    74  	panic(fmt.Sprintf("CurrentSection not found for %q in lang %s", pt.p.Path(), pt.p.Lang()))
    75  }
    76  
    77  func (pt pageTree) FirstSection() page.Page {
    78  	s := pt.p.m.pathInfo.Dir()
    79  	if s == "/" {
    80  		return pt.p.s.home
    81  	}
    82  
    83  	for {
    84  		k, n := pt.p.s.pageMap.treePages.LongestPrefix(s, true, func(n contentNodeI) bool { return n.isContentNodeBranch() })
    85  		if n == nil {
    86  			return nil
    87  		}
    88  
    89  		// /blog
    90  		if strings.Count(k, "/") < 2 {
    91  			return n.(page.Page)
    92  		}
    93  
    94  		if s == "" {
    95  			return nil
    96  		}
    97  
    98  		s = paths.Dir(s)
    99  
   100  	}
   101  }
   102  
   103  func (pt pageTree) InSection(other any) bool {
   104  	if pt.p == nil || types.IsNil(other) {
   105  		return false
   106  	}
   107  
   108  	p, ok := other.(page.Page)
   109  	if !ok {
   110  		return false
   111  	}
   112  
   113  	return pt.CurrentSection() == p.CurrentSection()
   114  }
   115  
   116  func (pt pageTree) Parent() page.Page {
   117  	if pt.p.IsHome() {
   118  		return nil
   119  	}
   120  
   121  	dir := pt.p.m.pathInfo.ContainerDir()
   122  
   123  	if dir == "" {
   124  		return pt.p.s.home
   125  	}
   126  
   127  	_, n := pt.p.s.pageMap.treePages.LongestPrefix(dir, true, nil)
   128  	if n != nil {
   129  		return n.(page.Page)
   130  	}
   131  	return nil
   132  }
   133  
   134  func (pt pageTree) Ancestors() page.Pages {
   135  	var ancestors page.Pages
   136  	parent := pt.Parent()
   137  	for parent != nil {
   138  		ancestors = append(ancestors, parent)
   139  		parent = parent.Parent()
   140  	}
   141  	return ancestors
   142  }
   143  
   144  func (pt pageTree) Sections() page.Pages {
   145  	var (
   146  		pages               page.Pages
   147  		currentBranchPrefix string
   148  		s                   = pt.p.Path()
   149  		prefix              = paths.AddTrailingSlash(s)
   150  		tree                = pt.p.s.pageMap.treePages
   151  	)
   152  
   153  	w := &doctree.NodeShiftTreeWalker[contentNodeI]{
   154  		Tree:   tree,
   155  		Prefix: prefix,
   156  	}
   157  	w.Handle = func(ss string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
   158  		if !n.isContentNodeBranch() {
   159  			return false, nil
   160  		}
   161  		if currentBranchPrefix == "" || !strings.HasPrefix(ss, currentBranchPrefix) {
   162  			if p, ok := n.(*pageState); ok && p.IsSection() && p.m.shouldList(false) && p.Parent() == pt.p {
   163  				pages = append(pages, p)
   164  			} else {
   165  				w.SkipPrefix(ss + "/")
   166  			}
   167  		}
   168  		currentBranchPrefix = ss + "/"
   169  		return false, nil
   170  	}
   171  
   172  	if err := w.Walk(context.Background()); err != nil {
   173  		panic(err)
   174  	}
   175  
   176  	page.SortByDefault(pages)
   177  	return pages
   178  }
   179  
   180  func (pt pageTree) Page() page.Page {
   181  	return pt.p
   182  }
   183  
   184  func (p pageTree) SectionsEntries() []string {
   185  	sp := p.SectionsPath()
   186  	if sp == "/" {
   187  		return nil
   188  	}
   189  	entries := strings.Split(sp[1:], "/")
   190  	if len(entries) == 0 {
   191  		return nil
   192  	}
   193  	return entries
   194  }
   195  
   196  func (p pageTree) SectionsPath() string {
   197  	return p.CurrentSection().Path()
   198  }