summaryrefslogtreecommitdiffstats
path: root/helpers
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-11-06 20:10:47 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-11-23 14:12:24 +0100
commitbfb9613a14ab2d93a4474e5486d22e52a9d5e2b3 (patch)
tree81c4dbd10505e952489e1dbcf1d7bafc88b57c28 /helpers
parenta3fe5e5e35f311f22b6b4fc38abfcf64cd2c7d6f (diff)
Add Goldmark as the new default markdown handler
This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo. If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration: ```toml [markup] defaultMarkdownHandler="blackfriday" ``` Fixes #5963 Fixes #1778 Fixes #6355
Diffstat (limited to 'helpers')
-rw-r--r--helpers/content.go45
-rw-r--r--helpers/general.go27
-rw-r--r--helpers/general_test.go13
-rw-r--r--helpers/pygments.go402
-rw-r--r--helpers/pygments_test.go300
5 files changed, 26 insertions, 761 deletions
diff --git a/helpers/content.go b/helpers/content.go
index 357bd48e7..941475461 100644
--- a/helpers/content.go
+++ b/helpers/content.go
@@ -32,7 +32,6 @@ import (
bp "github.com/gohugoio/hugo/bufferpool"
"github.com/gohugoio/hugo/config"
"github.com/spf13/afero"
- jww "github.com/spf13/jwalterweatherman"
"strings"
)
@@ -58,9 +57,6 @@ type ContentSpec struct {
BuildExpired bool
BuildDrafts bool
- Highlight func(code, lang, optsStr string) (string, error)
- defatultPygmentsOpts map[string]string
-
Cfg config.Provider
}
@@ -77,36 +73,10 @@ func NewContentSpec(cfg config.Provider, logger *loggers.Logger, contentFs afero
Cfg: cfg,
}
- // Highlighting setup
- options, err := parseDefaultPygmentsOpts(cfg)
- if err != nil {
- return nil, err
- }
- spec.defatultPygmentsOpts = options
-
- // Use the Pygmentize on path if present
- useClassic := false
- h := newHiglighters(spec)
-
- if cfg.GetBool("pygmentsUseClassic") {
- if !hasPygments() {
- jww.WARN.Println("Highlighting with pygmentsUseClassic set requires Pygments to be installed and in the path")
- } else {
- useClassic = true
- }
- }
-
- if useClassic {
- spec.Highlight = h.pygmentsHighlight
- } else {
- spec.Highlight = h.chromaHighlight
- }
-
converterProvider, err := markup.NewConverterProvider(converter.ProviderConfig{
Cfg: cfg,
ContentFs: contentFs,
Logger: logger,
- Highlight: spec.Highlight,
})
if err != nil {
return nil, err
@@ -220,6 +190,21 @@ func (c *ContentSpec) RenderMarkdown(src []byte) ([]byte, error) {
return b.Bytes(), nil
}
+func (c *ContentSpec) ResolveMarkup(in string) string {
+ in = strings.ToLower(in)
+ switch in {
+ case "md", "markdown", "mdown":
+ return "markdown"
+ case "html", "htm":
+ return "html"
+ default:
+ if conv := c.Converters.Get(in); conv != nil {
+ return conv.Name()
+ }
+ }
+ return ""
+}
+
// TotalWords counts instance of one or more consecutive white space
// characters, as defined by unicode.IsSpace, in s.
// This is a cheaper way of word counting than the obvious len(strings.Fields(s)).
diff --git a/helpers/general.go b/helpers/general.go
index 80c0591b2..259bcf8f4 100644
--- a/helpers/general.go
+++ b/helpers/general.go
@@ -44,11 +44,6 @@ import (
// FilePathSeparator as defined by os.Separator.
const FilePathSeparator = string(filepath.Separator)
-// Strips carriage returns from third-party / external processes (useful for Windows)
-func normalizeExternalHelperLineFeeds(content []byte) []byte {
- return bytes.Replace(content, []byte("\r"), []byte(""), -1)
-}
-
// FindAvailablePort returns an available and valid TCP port.
func FindAvailablePort() (*net.TCPAddr, error) {
l, err := net.Listen("tcp", ":0")
@@ -74,28 +69,6 @@ func InStringArray(arr []string, el string) bool {
return false
}
-// GuessType attempts to guess the type of file from a given string.
-func GuessType(in string) string {
- switch strings.ToLower(in) {
- case "md", "markdown", "mdown":
- return "markdown"
- case "asciidoc", "adoc", "ad":
- return "asciidoc"
- case "mmark":
- return "mmark"
- case "rst":
- return "rst"
- case "pandoc", "pdc":
- return "pandoc"
- case "html", "htm":
- return "html"
- case "org":
- return "org"
- }
-
- return ""
-}
-
// FirstUpper returns a string with the first character as upper case.
func FirstUpper(s string) string {
if s == "" {
diff --git a/helpers/general_test.go b/helpers/general_test.go
index e5e5e56ed..b8a98fb69 100644
--- a/helpers/general_test.go
+++ b/helpers/general_test.go
@@ -19,11 +19,20 @@ import (
"strings"
"testing"
+ "github.com/spf13/viper"
+
+ "github.com/gohugoio/hugo/common/loggers"
+
qt "github.com/frankban/quicktest"
"github.com/spf13/afero"
)
-func TestGuessType(t *testing.T) {
+func TestResolveMarkup(t *testing.T) {
+ c := qt.New(t)
+ cfg := viper.New()
+ spec, err := NewContentSpec(cfg, loggers.NewErrorLogger(), afero.NewMemMapFs())
+ c.Assert(err, qt.IsNil)
+
for i, this := range []struct {
in string
expect string
@@ -43,7 +52,7 @@ func TestGuessType(t *testing.T) {
{"org", "org"},
{"excel", ""},
} {
- result := GuessType(this.in)
+ result := spec.ResolveMarkup(this.in)
if result != this.expect {
t.Errorf("[%d] got %s but expected %s", i, result, this.expect)
}
diff --git a/helpers/pygments.go b/helpers/pygments.go
deleted file mode 100644
index 4d7631ced..000000000
--- a/helpers/pygments.go
+++ /dev/null
@@ -1,402 +0,0 @@
-// Copyright 2019 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package helpers
-
-import (
- "bytes"
- "crypto/sha1"
- "fmt"
- "io"
- "io/ioutil"
- "os/exec"
- "path/filepath"
- "regexp"
- "sort"
- "strconv"
- "strings"
-
- "github.com/alecthomas/chroma"
- "github.com/alecthomas/chroma/formatters"
- "github.com/alecthomas/chroma/formatters/html"
- "github.com/alecthomas/chroma/lexers"
- "github.com/alecthomas/chroma/styles"
- bp "github.com/gohugoio/hugo/bufferpool"
-
- "github.com/gohugoio/hugo/config"
- "github.com/gohugoio/hugo/hugofs"
- jww "github.com/spf13/jwalterweatherman"
-)
-
-const pygmentsBin = "pygmentize"
-
-// hasPygments checks to see if Pygments is installed and available
-// on the system.
-func hasPygments() bool {
- if _, err := exec.LookPath(pygmentsBin); err != nil {
- return false
- }
- return true
-}
-
-type highlighters struct {
- cs *ContentSpec
- ignoreCache bool
- cacheDir string
-}
-
-func newHiglighters(cs *ContentSpec) highlighters {
- return highlighters{cs: cs, ignoreCache: cs.Cfg.GetBool("ignoreCache"), cacheDir: cs.Cfg.GetString("cacheDir")}
-}
-
-func (h highlighters) chromaHighlight(code, lang, optsStr string) (string, error) {
- opts, err := h.cs.parsePygmentsOpts(optsStr)
- if err != nil {
- jww.ERROR.Print(err.Error())
- return code, err
- }
-
- style, found := opts["style"]
- if !found || style == "" {
- style = "friendly"
- }
-
- f, err := h.cs.chromaFormatterFromOptions(opts)
- if err != nil {
- jww.ERROR.Print(err.Error())
- return code, err
- }
-
- b := bp.GetBuffer()
- defer bp.PutBuffer(b)
-
- err = chromaHighlight(b, code, lang, style, f)
- if err != nil {
- jww.ERROR.Printf("Highlight failed: %s\nLang: %q\nCode: \n%s", err, lang, code)
- return code, err
- }
-
- return h.injectCodeTag(`<div class="highlight">`+b.String()+"</div>", lang), nil
-}
-
-func (h highlighters) pygmentsHighlight(code, lang, optsStr string) (string, error) {
- options, err := h.cs.createPygmentsOptionsString(optsStr)
-
- if err != nil {
- jww.ERROR.Print(err.Error())
- return code, nil
- }
-
- // Try to read from cache first
- hash := sha1.New()
- io.WriteString(hash, code)
- io.WriteString(hash, lang)
- io.WriteString(hash, options)
-
- fs := hugofs.Os
-
- var cachefile string
-
- if !h.ignoreCache && h.cacheDir != "" {
- cachefile = filepath.Join(h.cacheDir, fmt.Sprintf("pygments-%x", hash.Sum(nil)))
-
- exists, err := Exists(cachefile, fs)
- if err != nil {
- jww.ERROR.Print(err.Error())
- return code, nil
- }
- if exists {
- f, err := fs.Open(cachefile)
- if err != nil {
- jww.ERROR.Print(err.Error())
- return code, nil
- }
-
- s, err := ioutil.ReadAll(f)
- if err != nil {
- jww.ERROR.Print(err.Error())
- return code, nil
- }
-
- return string(s), nil
- }
- }
-
- // No cache file, render and cache it
- var out bytes.Buffer
- var stderr bytes.Buffer
-
- var langOpt string
- if lang == "" {
- langOpt = "-g" // Try guessing the language
- } else {
- langOpt = "-l" + lang
- }
-
- cmd := exec.Command(pygmentsBin, langOpt, "-fhtml", "-O", options)
- cmd.Stdin = strings.NewReader(code)
- cmd.Stdout = &out
- cmd.Stderr = &stderr
-
- if err := cmd.Run(); err != nil {
- jww.ERROR.Print(stderr.String())
- return code, err
- }
-
- str := string(normalizeExternalHelperLineFeeds(out.Bytes()))
-
- str = h.injectCodeTag(str, lang)
-
- if !h.ignoreCache && cachefile != "" {
- // Write cache file
- if err := WriteToDisk(cachefile, strings.NewReader(str), fs); err != nil {
- jww.ERROR.Print(stderr.String())
- }
- }
-
- return str, nil
-}
-
-var preRe = regexp.MustCompile(`(?s)(.*?<pre.*?>)(.*?)(</pre>)`)
-
-func (h highlighters) injectCodeTag(code, lang string) string {
- if lang == "" {
- return code
- }
- codeTag := fmt.Sprintf(`<code class="language-%s" data-lang="%s">`, lang, lang)
- return preRe.ReplaceAllString(code, fmt.Sprintf("$1%s$2</code>$3", codeTag))
-}
-
-func chromaHighlight(w io.Writer, source, lexer, style string, f chroma.Formatter) error {
- l := lexers.Get(lexer)
- if l == nil {
- l = lexers.Analyse(source)
- }
- if l == nil {
- l = lexers.Fallback
- }
- l = chroma.Coalesce(l)
-
- if f == nil {
- f = formatters.Fallback
- }
-
- s := styles.Get(style)
- if s == nil {
- s = styles.Fallback
- }
-
- it, err := l.Tokenise(nil, source)
- if err != nil {
- return err
- }
-
- return f.Format(w, s, it)
-}
-
-var pygmentsKeywords = make(map[string]bool)
-
-func init() {
- pygmentsKeywords["encoding"] = true
- pygmentsKeywords["outencoding"] = true
- pygmentsKeywords["nowrap"] = true
- pygmentsKeywords["full"] = true
- pygmentsKeywords["title"] = true
- pygmentsKeywords["style"] = true
- pygmentsKeywords["noclasses"] = true
- pygmentsKeywords["classprefix"] = true
- pygmentsKeywords["cssclass"] = true
- pygmentsKeywords["cssstyles"] = true
- pygmentsKeywords["prestyles"] = true
- pygmentsKeywords["linenos"] = true
- pygmentsKeywords["hl_lines"] = true
- pygmentsKeywords["linenostart"] = true
- pygmentsKeywords["linenostep"] = true
- pygmentsKeywords["linenospecial"] = true
- pygmentsKeywords["nobackground"] = true
- pygmentsKeywords["lineseparator"] = true
- pygmentsKeywords["lineanchors"] = true
- pygmentsKeywords["linespans"] = true
- pygmentsKeywords["anchorlinenos"] = true
- pygmentsKeywords["startinline"] = true
-}
-
-func parseOptions(defaults map[string]string, in string) (map[string]string, error) {
- in = strings.Trim(in, " ")
- opts := make(map[string]string)
-
- for k, v := range defaults {
- opts[k] = v
- }
-
- if in == "" {
- return opts, nil
- }
-
- for _, v := range strings.Split(in, ",") {
- keyVal := strings.Split(v, "=")
- key := strings.ToLower(strings.Trim(keyVal[0], " "))
- if len(keyVal) != 2 || !pygmentsKeywords[key] {
- return opts, fmt.Errorf("invalid Pygments option: %s", key)
- }
- opts[key] = keyVal[1]
- }
-
- return opts, nil
-}
-
-func createOptionsString(options map[string]string) string {
- var keys []string
- for k := range options {
- keys = append(keys, k)
- }
- sort.Strings(keys)
-
- var optionsStr string
- for i, k := range keys {
- optionsStr += fmt.Sprintf("%s=%s", k, options[k])
- if i < len(options)-1 {
- optionsStr += ","
- }
- }
-
- return optionsStr
-}
-
-func parseDefaultPygmentsOpts(cfg config.Provider) (map[string]string, error) {
- options, err := parseOptions(nil, cfg.GetString("pygmentsOptions"))
- if err != nil {
- return nil, err
- }
-
- if cfg.IsSet("pygmentsStyle") {
- options["style"] = cfg.GetString("pygmentsStyle")
- }
-
- if cfg.IsSet("pygmentsUseClasses") {
- if cfg.GetBool("pygmentsUseClasses") {
- options["noclasses"] = "false"
- } else {
- options["noclasses"] = "true"
- }
-
- }
-
- if _, ok := options["encoding"]; !ok {
- options["encoding"] = "utf8"
- }
-
- return options, nil
-}
-
-func (cs *ContentSpec) chromaFormatterFromOptions(pygmentsOpts map[string]string) (chroma.Formatter, error) {
- var options = []html.Option{html.TabWidth(4)}
-
- if pygmentsOpts["noclasses"] == "false" {
- options = append(options, html.WithClasses())
- }
-
- lineNumbers := pygmentsOpts["linenos"]
- if lineNumbers != "" {
- options = append(options, html.WithLineNumbers())
- if lineNumbers != "inline" {
- options = append(options, html.LineNumbersInTable())
- }
- }
-
- startLineStr := pygmentsOpts["linenostart"]
- var startLine = 1
- if startLineStr != "" {
-
- line, err := strconv.Atoi(strings.TrimSpace(startLineStr))
- if err == nil {
- startLine = line
- options = append(options, html.BaseLineNumber(startLine))
- }
- }
-
- hlLines := pygmentsOpts["hl_lines"]
-
- if hlLines != "" {
- ranges, err := hlLinesToRanges(startLine, hlLines)
-
- if err == nil {
- options = append(options, html.HighlightLines(ranges))
- }
- }
-
- return html.New(options...), nil
-}
-
-func (cs *ContentSpec) parsePygmentsOpts(in string) (map[string]string, error) {
- opts, err := parseOptions(cs.defatultPygmentsOpts, in)
- if err != nil {
- return nil, err
- }
- return opts, nil
-
-}
-
-func (cs *ContentSpec) createPygmentsOptionsString(in string) (string, error) {
- opts, err := cs.parsePygmentsOpts(in)
- if err != nil {
- return "", err
- }
- return createOptionsString(opts), nil
-}
-
-// startLine compansates for https://github.com/alecthomas/chroma/issues/30
-func hlLinesToRanges(startLine int, s string) ([][2]int, error) {
- var ranges [][2]int
- s = strings.TrimSpace(s)
-
- if s == "" {
- return ranges, nil
- }
-
- // Variants:
- // 1 2 3 4
- // 1-2 3-4
- // 1-2 3
- // 1 3-4
- // 1 3-4
- fields := strings.Split(s, " ")
- for _, field := range fields {
- field = strings.TrimSpace(field)
- if field == "" {
- continue
- }
- numbers := strings.Split(field, "-")
- var r [2]int
- first, err := strconv.Atoi(numbers[0])
- if err != nil {
- return ranges, err
- }
- first = first + startLine - 1
- r[0] = first
- if len(numbers) > 1 {
- second, err := strconv.Atoi(numbers[1])
- if err != nil {
- return ranges, err
- }
- second = second + startLine - 1
- r[1] = second
- } else {
- r[1] = first
- }
-
- ranges = append(ranges, r)
- }
- return ranges, nil
-
-}
diff --git a/helpers/pygments_test.go b/helpers/pygments_test.go
deleted file mode 100644
index 05d86e104..000000000
--- a/helpers/pygments_test.go
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright 2015 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package helpers
-
-import (
- "fmt"
- "reflect"
- "testing"
-
- "github.com/alecthomas/chroma/formatters/html"
-
- qt "github.com/frankban/quicktest"
- "github.com/spf13/viper"
-)
-
-func TestParsePygmentsArgs(t *testing.T) {
- c := qt.New(t)
-
- for i, this := range []struct {
- in string
- pygmentsStyle string
- pygmentsUseClasses bool
- expect1 interface{}
- }{
- {"", "foo", true, "encoding=utf8,noclasses=false,style=foo"},
- {"style=boo,noclasses=true", "foo", true, "encoding=utf8,noclasses=true,style=boo"},
- {"Style=boo, noClasses=true", "foo", true, "encoding=utf8,noclasses=true,style=boo"},
- {"noclasses=true", "foo", true, "encoding=utf8,noclasses=true,style=foo"},
- {"style=boo", "foo", true, "encoding=utf8,noclasses=false,style=boo"},
- {"boo=invalid", "foo", false, false},
- {"style", "foo", false, false},
- } {
-
- v := viper.New()
- v.Set("pygmentsStyle", this.pygmentsStyle)
- v.Set("pygmentsUseClasses", this.pygmentsUseClasses)
- spec, err := NewContentSpec(v, nil, nil)
- c.Assert(err, qt.IsNil)
-
- result1, err := spec.createPygmentsOptionsString(this.in)
- if b, ok := this.expect1.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] parsePygmentArgs didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] parsePygmentArgs failed: %s", i, err)
- continue
- }
- if result1 != this.expect1 {
- t.Errorf("[%d] parsePygmentArgs got %v but expected %v", i, result1, this.expect1)
- }
-
- }
- }
-}
-
-func TestParseDefaultPygmentsArgs(t *testing.T) {
- c := qt.New(t)
-
- expect := "encoding=utf8,noclasses=false,style=foo"
-
- for i, this := range []struct {
- in string
- pygmentsStyle interface{}
- pygmentsUseClasses interface{}
- pygmentsOptions string
- }{
- {"", "foo", true, "style=override,noclasses=override"},
- {"", nil, nil, "style=foo,noclasses=false"},
- {"style=foo,noclasses=false", nil, nil, "style=override,noclasses=override"},
- {"style=foo,noclasses=false", "override", false, "style=override,noclasses=override"},
- } {
- v := viper.New()
-
- v.Set("pygmentsOptions", this.pygmentsOptions)
-
- if s, ok := this.pygmentsStyle.(string); ok {
- v.Set("pygmentsStyle", s)
- }
-
- if b, ok := this.pygmentsUseClasses.(bool); ok {
- v.Set("pygmentsUseClasses", b)
- }
-
- spec, err := NewContentSpec(v, nil, nil)
- c.Assert(err, qt.IsNil)
-
- result, err := spec.createPygmentsOptionsString(this.in)
- if err != nil {
- t.Errorf("[%d] parsePygmentArgs failed: %s", i, err)
- continue
- }
- if result != expect {
- t.Errorf("[%d] parsePygmentArgs got %v but expected %v", i, result, expect)
- }
- }
-}
-
-type chromaInfo struct {
- classes bool
- lineNumbers bool
- lineNumbersInTable bool
- highlightRangesLen int
- highlightRangesStr string
- baseLineNumber int
-}
-
-func formatterChromaInfo(f *html.Formatter) chromaInfo {
- v := reflect.ValueOf(f).Elem()
- c := chromaInfo{}
- // Hack:
-
- c.classes = f.Classes
- c.lineNumbers = v.FieldByName("lineNumbers").Bool()
- c.lineNumbersInTable = v.FieldByName("lineNumbersInTable").Bool()
- c.baseLineNumber = int(v.FieldByName("baseLineNumber").Int())
- vv := v.FieldByName("highlightRanges")
- c.highlightRangesLen = vv.Len()
- c.highlightRangesStr = fmt.Sprint(vv)
-
- return c
-}
-
-func TestChromaHTMLHighlight(t *testing.T) {
- c := qt.New(t)
-
- v := viper.New()
- v.Set("pygmentsUseClasses", true)
- spec, err := NewContentSpec(v, nil, nil)
- c.Assert(err, qt.IsNil)
-
- result, err := spec.Highlight(`echo "Hello"`, "bash", "")
- c.Assert(err, qt.IsNil)
-
- c.Assert(result, qt.Contains, `<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="nb">echo</span> <span class="s2">&#34;Hello&#34;</span></code></pre></div>`)
-
-}
-
-func TestChromaHTMLFormatterFromOptions(t *testing.T) {
- c := qt.New(t)
-
- for i, this := range []struct {
- in string
- pygmentsStyle interface{}
- pygmentsUseClasses interface{}
- pygmentsOptions string
- assert func(c chromaInfo)
- }{
- {"", "monokai", true, "style=manni,noclasses=true", func(ci chromaInfo) {
- c.Assert(ci.classes, qt.Equals, true)
- c.Assert(ci.lineNumbers, qt.Equals, false)
- c.Assert(ci.highlightRangesLen, qt.Equals, 0)
-
- }},
- {"", nil, nil, "style=monokai,noclasses=false", func(ci chromaInfo) {
- c.Assert(ci.classes, qt.Equals, true)
- }},
- {"linenos=sure,hl_lines=1 2 3", nil, nil, "style=monokai,noclasses=false", func(ci chromaInfo) {
- c.Assert(ci.classes, qt.Equals, true)
- c.Assert(ci.lineNumbers, qt.Equals, true)
- c.Assert(ci.highlightRangesLen, qt.Equals, 3)
- c.Assert(ci.highlightRangesStr, qt.Equals, "[[1 1] [2 2] [3 3]]")
- c.Assert(ci.baseLineNumber, qt.Equals, 1)
- }},
- {"linenos=inline,hl_lines=1,linenostart=4", nil, nil, "style=monokai,noclasses=false", func(ci chromaInfo) {
- c.Assert(ci.classes, qt.Equals, true)
- c.Assert(ci.lineNumbers, qt.Equals, true)
- c.Assert(ci.lineNumbersInTable, qt.Equals, false)
- c.Assert(ci.highlightRangesLen, qt.Equals, 1)
- // This compansates for https://github.com/alecthomas/chroma/issues/30
- c.Assert(ci.highlightRangesStr, qt.Equals, "[[4 4]]")
- c.Assert(ci.baseLineNumber, qt.Equals, 4)
- }},
- {"linenos=table", nil, nil, "style=monokai", func(ci chromaInfo) {
- c.Assert(ci.lineNumbers, qt.Equals, true)
- c.Assert(ci.lineNumbersInTable, qt.Equals, true)
- }},
- {"style=monokai,noclasses=false", nil, nil, "style=manni,noclasses=true", func(ci chromaInfo) {
- c.Assert(ci.classes, qt.Equals, true)
- }},
- {"style=monokai,noclasses=true", "friendly", false, "style=manni,noclasses=false", func(ci chromaInfo) {
- c.Assert(ci.classes, qt.Equals, false)
- }},
- } {
- v := viper.New()
-
- v.Set("pygmentsOptions", this.pygmentsOptions)
-
- if s, ok := this.pygmentsStyle.(string); ok {
- v.Set("pygmentsStyle", s)
- }
-
- if b, ok := this.pygmentsUseClasses.(bool); ok {
- v.Set("pygmentsUseClasses", b)
- }
-
- spec, err := NewContentSpec(v, nil, nil)
- c.Assert(err, qt.IsNil)
-
- opts, err := spec.parsePygmentsOpts(this.in)
- if err != nil {
- t.Fatalf("[%d] parsePygmentsOpts failed: %s", i, err)
- }
-
- chromaFormatter, err := spec.chromaFormatterFromOptions(opts)
- if err != nil {
- t.Fatalf("[%d] chromaFormatterFromOptions failed: %s", i, err)
- }
-
- this.assert(formatterChromaInfo(chromaFormatter.(*html.Formatter)))
- }
-}
-
-func TestHlLinesToRanges(t *testing.T) {
- var zero [][2]int
-
- for _, this := range []struct {
- in string
- startLine int
- expected interface{}
- }{
- {"", 1, zero},
- {"1 4", 1, [][2]int{{1, 1}, {4, 4}}},
- {"1 4", 2, [][2]int{{2, 2}, {5, 5}}},
- {"1-4 5-8", 1, [][2]int{{1, 4}, {5, 8}}},
- {" 1 4 ", 1, [][2]int{{1, 1}, {4, 4}}},
- {"1-4 5-8 ", 1, [][2]int{{1, 4}, {5, 8}}},
- {"1-4 5", 1, [][2]int{{1, 4}, {5, 5}}},
- {"4 5-9", 1, [][2]int{{4, 4}, {5, 9}}},
- {" 1 -4 5 - 8 ", 1, true},
- {"a b", 1, true},
- } {
- got, err := hlLinesToRanges(this.startLine, this.in)
-
- if expectErr, ok := this.expected.(bool); ok && expectErr {
- if err == nil {
- t.Fatal("No error")
- }
- } else if err != nil {
- t.Fatalf("Got error: %s", err)
- } else if !reflect.DeepEqual(this.expected, got) {
- t.Fatalf("Expected\n%v but got\n%v", this.expected, got)
- }
- }
-}
-
-func BenchmarkChromaHighlight(b *testing.B) {
- c := qt.New(b)
- v := viper.New()
-
- v.Set("pygmentsstyle", "trac")
- v.Set("pygmentsuseclasses", false)
- v.Set("pygmentsuseclassic", false)
-
- code := `// GetTitleFunc returns a func that can be used to transform a string to
-// title case.
-//
-// The supported styles are
-//
-// - "Go" (strings.Title)
-// - "AP" (see https://www.apstylebook.com/)
-// - "Chicago" (see http://www.chicagomanualofstyle.org/home.html)
-//
-// If an unknown or empty style is provided, AP style is what you get.
-func GetTitleFunc(style string) func(s string) string {
- switch strings.ToLower(style) {
- case "go":
- return strings.Title
- case "chicago":
- tc := transform.NewTitleConverter(transform.ChicagoStyle)
- return tc.Title
- default:
- tc := transform.NewTitleConverter(transform.APStyle)
- return tc.Title
- }
-}
-`
-
- spec, err := NewContentSpec(v, nil, nil)
- c.Assert(err, qt.IsNil)
-
- for i := 0; i < b.N; i++ {
- _, err := spec.Highlight(code, "go", "linenos=inline,hl_lines=8 15-17")
- if err != nil {
- b.Fatal(err)
- }
- }
-}