github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/markup/blackfriday/convert.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 blackfriday converts Markdown to HTML using Blackfriday v1. 15 package blackfriday 16 17 import ( 18 "unicode" 19 20 "github.com/gohugoio/hugo/identity" 21 "github.com/gohugoio/hugo/markup/blackfriday/blackfriday_config" 22 "github.com/gohugoio/hugo/markup/converter" 23 "github.com/russross/blackfriday" 24 ) 25 26 // Provider is the package entry point. 27 var Provider converter.ProviderProvider = provider{} 28 29 type provider struct { 30 } 31 32 func (p provider) New(cfg converter.ProviderConfig) (converter.Provider, error) { 33 defaultExtensions := getMarkdownExtensions(cfg.MarkupConfig.BlackFriday) 34 35 return converter.NewProvider("blackfriday", func(ctx converter.DocumentContext) (converter.Converter, error) { 36 b := cfg.MarkupConfig.BlackFriday 37 extensions := defaultExtensions 38 39 if ctx.ConfigOverrides != nil { 40 var err error 41 b, err = blackfriday_config.UpdateConfig(b, ctx.ConfigOverrides) 42 if err != nil { 43 return nil, err 44 } 45 extensions = getMarkdownExtensions(b) 46 } 47 48 return &blackfridayConverter{ 49 ctx: ctx, 50 bf: b, 51 extensions: extensions, 52 cfg: cfg, 53 }, nil 54 }), nil 55 } 56 57 type blackfridayConverter struct { 58 ctx converter.DocumentContext 59 bf blackfriday_config.Config 60 extensions int 61 cfg converter.ProviderConfig 62 } 63 64 func (c *blackfridayConverter) SanitizeAnchorName(s string) string { 65 return SanitizedAnchorName(s) 66 } 67 68 // SanitizedAnchorName is how Blackfriday sanitizes anchor names. 69 // Implementation borrowed from https://github.com/russross/blackfriday/blob/a477dd1646916742841ed20379f941cfa6c5bb6f/block.go#L1464 70 func SanitizedAnchorName(text string) string { 71 var anchorName []rune 72 futureDash := false 73 for _, r := range text { 74 switch { 75 case unicode.IsLetter(r) || unicode.IsNumber(r): 76 if futureDash && len(anchorName) > 0 { 77 anchorName = append(anchorName, '-') 78 } 79 futureDash = false 80 anchorName = append(anchorName, unicode.ToLower(r)) 81 default: 82 futureDash = true 83 } 84 } 85 return string(anchorName) 86 } 87 88 func (c *blackfridayConverter) AnchorSuffix() string { 89 if c.bf.PlainIDAnchors { 90 return "" 91 } 92 return ":" + c.ctx.DocumentID 93 } 94 95 func (c *blackfridayConverter) Convert(ctx converter.RenderContext) (converter.Result, error) { 96 r := c.getHTMLRenderer(ctx.RenderTOC) 97 98 return converter.Bytes(blackfriday.Markdown(ctx.Src, r, c.extensions)), nil 99 } 100 101 func (c *blackfridayConverter) Supports(feature identity.Identity) bool { 102 return false 103 } 104 105 func (c *blackfridayConverter) getHTMLRenderer(renderTOC bool) blackfriday.Renderer { 106 flags := getFlags(renderTOC, c.bf) 107 108 documentID := c.ctx.DocumentID 109 110 renderParameters := blackfriday.HtmlRendererParameters{ 111 FootnoteAnchorPrefix: c.bf.FootnoteAnchorPrefix, 112 FootnoteReturnLinkContents: c.bf.FootnoteReturnLinkContents, 113 } 114 115 if documentID != "" && !c.bf.PlainIDAnchors { 116 renderParameters.FootnoteAnchorPrefix = documentID + ":" + renderParameters.FootnoteAnchorPrefix 117 renderParameters.HeaderIDSuffix = ":" + documentID 118 } 119 120 return &hugoHTMLRenderer{ 121 c: c, 122 Renderer: blackfriday.HtmlRendererWithParameters(flags, "", "", renderParameters), 123 } 124 } 125 126 func getFlags(renderTOC bool, cfg blackfriday_config.Config) int { 127 var flags int 128 129 if renderTOC { 130 flags = blackfriday.HTML_TOC 131 } 132 133 flags |= blackfriday.HTML_USE_XHTML 134 flags |= blackfriday.HTML_FOOTNOTE_RETURN_LINKS 135 136 if cfg.Smartypants { 137 flags |= blackfriday.HTML_USE_SMARTYPANTS 138 } 139 140 if cfg.SmartypantsQuotesNBSP { 141 flags |= blackfriday.HTML_SMARTYPANTS_QUOTES_NBSP 142 } 143 144 if cfg.AngledQuotes { 145 flags |= blackfriday.HTML_SMARTYPANTS_ANGLED_QUOTES 146 } 147 148 if cfg.Fractions { 149 flags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS 150 } 151 152 if cfg.HrefTargetBlank { 153 flags |= blackfriday.HTML_HREF_TARGET_BLANK 154 } 155 156 if cfg.NofollowLinks { 157 flags |= blackfriday.HTML_NOFOLLOW_LINKS 158 } 159 160 if cfg.NoreferrerLinks { 161 flags |= blackfriday.HTML_NOREFERRER_LINKS 162 } 163 164 if cfg.SmartDashes { 165 flags |= blackfriday.HTML_SMARTYPANTS_DASHES 166 } 167 168 if cfg.LatexDashes { 169 flags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES 170 } 171 172 if cfg.SkipHTML { 173 flags |= blackfriday.HTML_SKIP_HTML 174 } 175 176 return flags 177 } 178 179 func getMarkdownExtensions(cfg blackfriday_config.Config) int { 180 // Default Blackfriday common extensions 181 commonExtensions := 0 | 182 blackfriday.EXTENSION_NO_INTRA_EMPHASIS | 183 blackfriday.EXTENSION_TABLES | 184 blackfriday.EXTENSION_FENCED_CODE | 185 blackfriday.EXTENSION_AUTOLINK | 186 blackfriday.EXTENSION_STRIKETHROUGH | 187 blackfriday.EXTENSION_SPACE_HEADERS | 188 blackfriday.EXTENSION_HEADER_IDS | 189 blackfriday.EXTENSION_BACKSLASH_LINE_BREAK | 190 blackfriday.EXTENSION_DEFINITION_LISTS 191 192 // Extra Blackfriday extensions that Hugo enables by default 193 flags := commonExtensions | 194 blackfriday.EXTENSION_AUTO_HEADER_IDS | 195 blackfriday.EXTENSION_FOOTNOTES 196 197 for _, extension := range cfg.Extensions { 198 if flag, ok := blackfridayExtensionMap[extension]; ok { 199 flags |= flag 200 } 201 } 202 for _, extension := range cfg.ExtensionsMask { 203 if flag, ok := blackfridayExtensionMap[extension]; ok { 204 flags &= ^flag 205 } 206 } 207 return flags 208 } 209 210 var blackfridayExtensionMap = map[string]int{ 211 "noIntraEmphasis": blackfriday.EXTENSION_NO_INTRA_EMPHASIS, 212 "tables": blackfriday.EXTENSION_TABLES, 213 "fencedCode": blackfriday.EXTENSION_FENCED_CODE, 214 "autolink": blackfriday.EXTENSION_AUTOLINK, 215 "strikethrough": blackfriday.EXTENSION_STRIKETHROUGH, 216 "laxHtmlBlocks": blackfriday.EXTENSION_LAX_HTML_BLOCKS, 217 "spaceHeaders": blackfriday.EXTENSION_SPACE_HEADERS, 218 "hardLineBreak": blackfriday.EXTENSION_HARD_LINE_BREAK, 219 "tabSizeEight": blackfriday.EXTENSION_TAB_SIZE_EIGHT, 220 "footnotes": blackfriday.EXTENSION_FOOTNOTES, 221 "noEmptyLineBeforeBlock": blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK, 222 "headerIds": blackfriday.EXTENSION_HEADER_IDS, 223 "titleblock": blackfriday.EXTENSION_TITLEBLOCK, 224 "autoHeaderIds": blackfriday.EXTENSION_AUTO_HEADER_IDS, 225 "backslashLineBreak": blackfriday.EXTENSION_BACKSLASH_LINE_BREAK, 226 "definitionLists": blackfriday.EXTENSION_DEFINITION_LISTS, 227 "joinLines": blackfriday.EXTENSION_JOIN_LINES, 228 } 229 230 var ( 231 _ converter.DocumentInfo = (*blackfridayConverter)(nil) 232 _ converter.AnchorNameSanitizer = (*blackfridayConverter)(nil) 233 )