github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/hugolib/page__content.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 20 "github.com/gohugoio/hugo/output" 21 "github.com/gohugoio/hugo/parser/pageparser" 22 ) 23 24 var ( 25 internalSummaryDividerBase = "HUGOMORE42" 26 internalSummaryDividerBaseBytes = []byte(internalSummaryDividerBase) 27 internalSummaryDividerPre = []byte("\n\n" + internalSummaryDividerBase + "\n\n") 28 ) 29 30 // The content related items on a Page. 31 type pageContent struct { 32 selfLayout string 33 truncated bool 34 35 cmap *pageContentMap 36 37 source rawPageContent 38 } 39 40 // returns the content to be processed by Goldmark or similar. 41 func (p pageContent) contentToRender(ctx context.Context, parsed pageparser.Result, pm *pageContentMap, renderedShortcodes map[string]shortcodeRenderer) ([]byte, bool, error) { 42 source := parsed.Input() 43 var hasVariants bool 44 c := make([]byte, 0, len(source)+(len(source)/10)) 45 46 for _, it := range pm.items { 47 switch v := it.(type) { 48 case pageparser.Item: 49 c = append(c, source[v.Pos():v.Pos()+len(v.Val(source))]...) 50 case pageContentReplacement: 51 c = append(c, v.val...) 52 case *shortcode: 53 if !v.insertPlaceholder() { 54 // Insert the rendered shortcode. 55 renderedShortcode, found := renderedShortcodes[v.placeholder] 56 if !found { 57 // This should never happen. 58 panic(fmt.Sprintf("rendered shortcode %q not found", v.placeholder)) 59 } 60 61 b, more, err := renderedShortcode.renderShortcode(ctx) 62 if err != nil { 63 return nil, false, fmt.Errorf("failed to render shortcode: %w", err) 64 } 65 hasVariants = hasVariants || more 66 c = append(c, []byte(b)...) 67 68 } else { 69 // Insert the placeholder so we can insert the content after 70 // markdown processing. 71 c = append(c, []byte(v.placeholder)...) 72 } 73 default: 74 panic(fmt.Sprintf("unknown item type %T", it)) 75 } 76 } 77 78 return c, hasVariants, nil 79 } 80 81 func (p pageContent) selfLayoutForOutput(f output.Format) string { 82 if p.selfLayout == "" { 83 return "" 84 } 85 return p.selfLayout + f.Name 86 } 87 88 type rawPageContent struct { 89 hasSummaryDivider bool 90 91 // The AST of the parsed page. Contains information about: 92 // shortcodes, front matter, summary indicators. 93 parsed pageparser.Result 94 95 // Returns the position in bytes after any front matter. 96 posMainContent int 97 98 // These are set if we're able to determine this from the source. 99 posSummaryEnd int 100 posBodyStart int 101 } 102 103 type pageContentReplacement struct { 104 val []byte 105 106 source pageparser.Item 107 } 108 109 type pageContentMap struct { 110 111 // If not, we can skip any pre-rendering of shortcodes. 112 hasMarkdownShortcode bool 113 114 // Indicates whether we must do placeholder replacements. 115 hasNonMarkdownShortcode bool 116 117 // *shortcode, pageContentReplacement or pageparser.Item 118 items []any 119 } 120 121 func (p *pageContentMap) AddBytes(item pageparser.Item) { 122 p.items = append(p.items, item) 123 } 124 125 func (p *pageContentMap) AddReplacement(val []byte, source pageparser.Item) { 126 p.items = append(p.items, pageContentReplacement{val: val, source: source}) 127 } 128 129 func (p *pageContentMap) AddShortcode(s *shortcode) { 130 p.items = append(p.items, s) 131 if s.insertPlaceholder() { 132 p.hasNonMarkdownShortcode = true 133 } else { 134 p.hasMarkdownShortcode = true 135 } 136 }