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 }