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  )