github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/markup/highlight/config.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 highlight provides code highlighting.
    15  package highlight
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  
    22  	"github.com/alecthomas/chroma/formatters/html"
    23  
    24  	"github.com/gohugoio/hugo/config"
    25  
    26  	"github.com/mitchellh/mapstructure"
    27  )
    28  
    29  var DefaultConfig = Config{
    30  	// The highlighter style to use.
    31  	// See https://xyproto.github.io/splash/docs/all.html
    32  	Style:              "monokai",
    33  	LineNoStart:        1,
    34  	CodeFences:         true,
    35  	NoClasses:          true,
    36  	LineNumbersInTable: true,
    37  	TabWidth:           4,
    38  }
    39  
    40  //
    41  type Config struct {
    42  	Style string
    43  
    44  	CodeFences bool
    45  
    46  	// Use inline CSS styles.
    47  	NoClasses bool
    48  
    49  	// When set, line numbers will be printed.
    50  	LineNos            bool
    51  	LineNumbersInTable bool
    52  
    53  	// When set, add links to line numbers
    54  	AnchorLineNos bool
    55  	LineAnchors   string
    56  
    57  	// Start the line numbers from this value (default is 1).
    58  	LineNoStart int
    59  
    60  	// A space separated list of line numbers, e.g. “3-8 10-20”.
    61  	Hl_Lines string
    62  
    63  	// TabWidth sets the number of characters for a tab. Defaults to 4.
    64  	TabWidth int
    65  
    66  	GuessSyntax bool
    67  }
    68  
    69  func (cfg Config) ToHTMLOptions() []html.Option {
    70  	var lineAnchors string
    71  	if cfg.LineAnchors != "" {
    72  		lineAnchors = cfg.LineAnchors + "-"
    73  	}
    74  	options := []html.Option{
    75  		html.TabWidth(cfg.TabWidth),
    76  		html.WithLineNumbers(cfg.LineNos),
    77  		html.BaseLineNumber(cfg.LineNoStart),
    78  		html.LineNumbersInTable(cfg.LineNumbersInTable),
    79  		html.WithClasses(!cfg.NoClasses),
    80  		html.LinkableLineNumbers(cfg.AnchorLineNos, lineAnchors),
    81  	}
    82  
    83  	if cfg.Hl_Lines != "" {
    84  		ranges, err := hlLinesToRanges(cfg.LineNoStart, cfg.Hl_Lines)
    85  		if err == nil {
    86  			options = append(options, html.HighlightLines(ranges))
    87  		}
    88  	}
    89  
    90  	return options
    91  }
    92  
    93  func applyOptionsFromString(opts string, cfg *Config) error {
    94  	optsm, err := parseOptions(opts)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	return mapstructure.WeakDecode(optsm, cfg)
    99  }
   100  
   101  // ApplyLegacyConfig applies legacy config from back when we had
   102  // Pygments.
   103  func ApplyLegacyConfig(cfg config.Provider, conf *Config) error {
   104  	if conf.Style == DefaultConfig.Style {
   105  		if s := cfg.GetString("pygmentsStyle"); s != "" {
   106  			conf.Style = s
   107  		}
   108  	}
   109  
   110  	if conf.NoClasses == DefaultConfig.NoClasses && cfg.IsSet("pygmentsUseClasses") {
   111  		conf.NoClasses = !cfg.GetBool("pygmentsUseClasses")
   112  	}
   113  
   114  	if conf.CodeFences == DefaultConfig.CodeFences && cfg.IsSet("pygmentsCodeFences") {
   115  		conf.CodeFences = cfg.GetBool("pygmentsCodeFences")
   116  	}
   117  
   118  	if conf.GuessSyntax == DefaultConfig.GuessSyntax && cfg.IsSet("pygmentsCodefencesGuessSyntax") {
   119  		conf.GuessSyntax = cfg.GetBool("pygmentsCodefencesGuessSyntax")
   120  	}
   121  
   122  	if cfg.IsSet("pygmentsOptions") {
   123  		if err := applyOptionsFromString(cfg.GetString("pygmentsOptions"), conf); err != nil {
   124  			return err
   125  		}
   126  	}
   127  
   128  	return nil
   129  }
   130  
   131  func parseOptions(in string) (map[string]interface{}, error) {
   132  	in = strings.Trim(in, " ")
   133  	opts := make(map[string]interface{})
   134  
   135  	if in == "" {
   136  		return opts, nil
   137  	}
   138  
   139  	for _, v := range strings.Split(in, ",") {
   140  		keyVal := strings.Split(v, "=")
   141  		key := strings.ToLower(strings.Trim(keyVal[0], " "))
   142  		if len(keyVal) != 2 {
   143  			return opts, fmt.Errorf("invalid Highlight option: %s", key)
   144  		}
   145  		if key == "linenos" {
   146  			opts[key] = keyVal[1] != "false"
   147  			if keyVal[1] == "table" || keyVal[1] == "inline" {
   148  				opts["lineNumbersInTable"] = keyVal[1] == "table"
   149  			}
   150  		} else {
   151  			opts[key] = keyVal[1]
   152  		}
   153  	}
   154  
   155  	return opts, nil
   156  }
   157  
   158  // startLine compensates for https://github.com/alecthomas/chroma/issues/30
   159  func hlLinesToRanges(startLine int, s string) ([][2]int, error) {
   160  	var ranges [][2]int
   161  	s = strings.TrimSpace(s)
   162  
   163  	if s == "" {
   164  		return ranges, nil
   165  	}
   166  
   167  	// Variants:
   168  	// 1 2 3 4
   169  	// 1-2 3-4
   170  	// 1-2 3
   171  	// 1 3-4
   172  	// 1    3-4
   173  	fields := strings.Split(s, " ")
   174  	for _, field := range fields {
   175  		field = strings.TrimSpace(field)
   176  		if field == "" {
   177  			continue
   178  		}
   179  		numbers := strings.Split(field, "-")
   180  		var r [2]int
   181  		first, err := strconv.Atoi(numbers[0])
   182  		if err != nil {
   183  			return ranges, err
   184  		}
   185  		first = first + startLine - 1
   186  		r[0] = first
   187  		if len(numbers) > 1 {
   188  			second, err := strconv.Atoi(numbers[1])
   189  			if err != nil {
   190  				return ranges, err
   191  			}
   192  			second = second + startLine - 1
   193  			r[1] = second
   194  		} else {
   195  			r[1] = first
   196  		}
   197  
   198  		ranges = append(ranges, r)
   199  	}
   200  	return ranges, nil
   201  }