diff --git a/markup/highlight/config.go b/markup/highlight/config.go index 6407cdff6..184042ae5 100644 --- a/markup/highlight/config.go +++ b/markup/highlight/config.go @@ -148,22 +148,6 @@ func (cfg Config) toHTMLOptions() ([]html.Option, error) { return options, nil } -func applyOptions(opts any, cfg *Config) error { - if opts == nil { - return nil - } - switch vv := opts.(type) { - case map[string]any: - return applyOptionsFromMap(vv, cfg) - default: - s, err := cast.ToStringE(opts) - if err != nil { - return err - } - return applyOptionsFromString(s, cfg) - } -} - func applyOptionsFromString(opts string, cfg *Config) error { optsm, err := parseHighlightOptions(opts) if err != nil { @@ -177,6 +161,43 @@ func applyOptionsFromMap(optsm map[string]any, cfg *Config) error { return mapstructure.WeakDecode(optsm, cfg) } +// applyOptions applies opts (a string or a map) to cfg. The type and code +// options, if set, are not part of Config and instead override lang and code +// respectively. Shared by Highlight and HighlightCodeBlock. See issue 11872. +func applyOptions(opts any, cfg *Config, lang, code *string) error { + if opts == nil { + return nil + } + + var optsm map[string]any + switch vv := opts.(type) { + case map[string]any: + optsm = make(map[string]any, len(vv)) + for k, v := range vv { + optsm[strings.ToLower(k)] = v + } + default: + s, err := cast.ToStringE(opts) + if err != nil { + return err + } + if optsm, err = parseHighlightOptions(s); err != nil { + return err + } + } + + if v, found := optsm["type"]; found { + *lang = cast.ToString(v) + delete(optsm, "type") + } + if v, found := optsm["code"]; found { + *code = cast.ToString(v) + delete(optsm, "code") + } + + return applyOptionsFromMap(optsm, cfg) +} + func applyOptionsFromCodeBlockContext(ctx hooks.CodeblockContext, cfg *Config) error { if cfg.LineAnchors == "" { const lineAnchorPrefix = "hl-" diff --git a/markup/highlight/highlight.go b/markup/highlight/highlight.go index bb58b9ca0..77bc52cf0 100644 --- a/markup/highlight/highlight.go +++ b/markup/highlight/highlight.go @@ -71,7 +71,7 @@ type chromaHighlighter struct { func (h chromaHighlighter) Highlight(code, lang string, opts any) (string, error) { cfg := h.cfg - if err := applyOptions(opts, &cfg); err != nil { + if err := applyOptions(opts, &cfg, &lang, &code); err != nil { return "", err } var b strings.Builder @@ -90,14 +90,14 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts a attributes := ctx.(hooks.AttributesOptionsSliceProvider).AttributesSlice() - options := ctx.Options() - - if err := applyOptionsFromMap(options, &cfg); err != nil { + if err := applyOptionsFromMap(ctx.Options(), &cfg); err != nil { return HighlightResult{}, err } - // Apply these last so the user can override them. - if err := applyOptions(opts, &cfg); err != nil { + lang, code := ctx.Type(), ctx.Inner() + + // Apply these last so the user can override them, including the type and code. + if err := applyOptions(opts, &cfg, &lang, &code); err != nil { return HighlightResult{}, err } @@ -105,7 +105,7 @@ func (h chromaHighlighter) HighlightCodeBlock(ctx hooks.CodeblockContext, opts a return HighlightResult{}, err } - low, high, err := highlight(&b, ctx.Inner(), ctx.Type(), attributes, cfg) + low, high, err := highlight(&b, code, lang, attributes, cfg) if err != nil { return HighlightResult{}, err } diff --git a/markup/highlight/highlight_integration_test.go b/markup/highlight/highlight_integration_test.go index 19779fd2f..4f72b51c1 100644 --- a/markup/highlight/highlight_integration_test.go +++ b/markup/highlight/highlight_integration_test.go @@ -80,6 +80,50 @@ HighlightCodeBlock: Wrapped:{{ $result.Wrapped }}|Inner:{{ $result.Inner }} ) } +// See issue 11872. +func TestCodeblockWithTypeOverride(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +[markup.highlight] +noClasses = false # to reduce size of assertion string +-- content/p1.md -- +--- +title: p1 +--- +§§§go {style=monokai class=my-class tabWidth=8} +i = 42 +§§§ +-- content/p2.md -- +--- +title: p2 +--- +§§§{style=monokai class=my-class tabWidth=8} +i = 42 +§§§ +-- layouts/page.html -- +{{ .Content }} +-- layouts/_markup/render-codeblock.html -- +{{- $opts := dict }} +{{- if not (transform.CanHighlight .Type) }} + {{- $opts = dict "type" "text" }} +{{- end }} +{{- $result := transform.HighlightCodeBlock . $opts }} +{{- $result.Wrapped -}} +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/p1/index.html", + `
i = 42i = 42i = 42