Compare commits

...

1 Commits

Author SHA1 Message Date
Bjørn Erik Pedersen 43cf8a8468 Add markup.rst.syntaxHighlight option
Fixes #5349
2026-07-03 15:50:20 +02:00
5 changed files with 175 additions and 4 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ jobs:
run: go install github.com/gohugoio/gotmplfmt@latest
- name: Install docutils
run: |
pip install docutils
pip install docutils Pygments
rst2html --version
- if: matrix.os == 'ubuntu-latest'
name: Install pandoc on Linux
+14 -1
View File
@@ -14,11 +14,14 @@
package markup_config
import (
"fmt"
"github.com/gohugoio/hugo/common/hmaps"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/markup/asciidocext/asciidocext_config"
"github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
"github.com/gohugoio/hugo/markup/highlight"
"github.com/gohugoio/hugo/markup/rst/rst_config"
"github.com/gohugoio/hugo/markup/tableofcontents"
"github.com/mitchellh/mapstructure"
)
@@ -39,10 +42,19 @@ type Config struct {
// Configuration for the AsciiDoc external markdown engine.
AsciiDocExt asciidocext_config.Config
// Configuration for the reStructuredText external markdown engine.
RST rst_config.Config
}
func (c *Config) Init() error {
return c.Goldmark.Init()
if err := c.Goldmark.Init(); err != nil {
return fmt.Errorf("goldmark: %s", err)
}
if err := c.RST.Init(); err != nil {
return fmt.Errorf("rst: %s", err)
}
return nil
}
func Decode(cfg config.Provider) (conf Config, err error) {
@@ -119,4 +131,5 @@ var Default = Config{
Goldmark: goldmark_config.Default,
AsciiDocExt: asciidocext_config.Default,
RST: rst_config.Default,
}
+20 -2
View File
@@ -17,6 +17,7 @@ package rst
import (
"bytes"
"fmt"
"os/exec"
"runtime"
"github.com/gohugoio/hugo/common/hexec"
@@ -71,6 +72,11 @@ func (c *rstConverter) getRstContent(src []byte, ctx converter.DocumentContext)
logger.Infoln("Rendering", ctx.DocumentName, "with", binaryName, "...")
args := []string{"--leave-comments", "--initial-header-level=2"}
if c.cfg.Conf != nil && c.cfg.MarkupConfig().RST.SyntaxHighlight != "long" {
args = append(args, "--syntax-highlight="+c.cfg.MarkupConfig().RST.SyntaxHighlight)
}
var result []byte
var err error
@@ -80,10 +86,9 @@ func (c *rstConverter) getRstContent(src []byte, ctx converter.DocumentContext)
// handle Windows manually because it doesn't do shebangs
if runtime.GOOS == "windows" {
pythonBinary, _ := internal.GetPythonBinaryAndExecPath()
args := []string{binaryPath, "--leave-comments", "--initial-header-level=2"}
args = append([]string{binaryPath}, args...)
result, err = internal.ExternallyRenderContent(c.cfg, ctx, src, pythonBinary, args)
} else {
args := []string{"--leave-comments", "--initial-header-level=2"}
result, err = internal.ExternallyRenderContent(c.cfg, ctx, src, binaryName, args)
}
@@ -128,3 +133,16 @@ func Supports() bool {
}
return hasBin
}
// SupportsPygments returns whether Pygments is (or should be) installed on this computer.
func SupportsPygments() bool {
for _, python := range []string{"python3", "python"} {
if err := exec.Command(python, "-c", "import pygments").Run(); err == nil {
return true
}
}
if htesting.SupportsAll() {
panic("Pygments not installed")
}
return false
}
+35
View File
@@ -0,0 +1,35 @@
// Copyright 2026 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 rst_config holds reStructuredText related configuration.
package rst_config
import "fmt"
// Default holds Hugo's default reStructuredText configuration.
var Default = Config{
SyntaxHighlight: "long",
}
// Config configures reStructuredText.
type Config struct {
// Configures Pygments syntax highlighting. Valid values are "short", "long" (default) and "none".
SyntaxHighlight string
}
func (c *Config) Init() error {
if c.SyntaxHighlight != "short" && c.SyntaxHighlight != "long" && c.SyntaxHighlight != "none" {
return fmt.Errorf("invalid value for syntaxHighlight: %q", c.SyntaxHighlight)
}
return nil
}
+105
View File
@@ -0,0 +1,105 @@
// Copyright 2026 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 rst_test
import (
"runtime"
"strings"
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/markup/rst"
)
// --syntax-highlight, default = long.
func TestRSTSyntaxHighlight(t *testing.T) {
if !rst.Supports() {
t.Skip("rst not installed")
}
if !rst.SupportsPygments() {
t.Skip("Pygments not installed")
}
execAllow := `'^rst2html(\.py)?$'`
if runtime.GOOS == "windows" {
execAllow = `'^python(\.exe)?$'`
}
filesTemplate := strings.ReplaceAll(`
-- hugo.toml --
baseURL = "https://example.org"
disableKinds = ["home", "section", "taxonomy", "term", "rss", "sitemap"]
markup.rst.syntaxHighlight = 'SYNTAX_HIGHLIGHT'
[security.exec]
allow = [RST_EXEC_ALLOW]
-- layouts/page.html --
{{ .Content }}
-- content/p.rst --
---
title: p
---
.. code:: go
if true {}
`, "RST_EXEC_ALLOW", execAllow)
files := strings.ReplaceAll(filesTemplate, "SYNTAX_HIGHLIGHT", "none")
b := hugolib.Test(t, files)
b.AssertFileContent("public/p/index.html", `
! Pygments
`)
b.AssertFileContent("public/p/index.html", `
if true {}
! if</span>
`)
files = strings.ReplaceAll(filesTemplate, "SYNTAX_HIGHLIGHT", "short")
b = hugolib.Test(t, files)
b.AssertFileContent("public/p/index.html", `
<span class="k">if</span>
! <span class="keyword">if</span>
`)
files = strings.ReplaceAll(filesTemplate, "SYNTAX_HIGHLIGHT", "long")
b = hugolib.Test(t, files)
b.AssertFileContent("public/p/index.html", `
! <span class="k">if</span>
<span class="keyword">if</span>
`)
// Default long.
files = strings.ReplaceAll(filesTemplate, "markup.rst.syntaxHighlight = 'SYNTAX_HIGHLIGHT'", "")
b = hugolib.Test(t, files)
b.AssertFileContent("public/p/index.html", `
! <span class="k">if</span>
<span class="keyword">if</span>
`)
// Invalid.
files = strings.ReplaceAll(filesTemplate, "SYNTAX_HIGHLIGHT", "foo")
b, err := hugolib.TestE(t, files)
b.Assert(err, qt.IsNotNil)
b.Assert(err.Error(), qt.Contains, "rst: invalid value for syntaxHighlight: \"foo\"")
}