Note that the config per language part of this will be handled in #10602.
Updates #9772
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/goldmark/codeblocks"
+ "github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
"github.com/gohugoio/hugo/markup/goldmark/images"
"github.com/gohugoio/hugo/markup/goldmark/internal/extensions/attributes"
"github.com/gohugoio/hugo/markup/goldmark/internal/render"
extensions = append(extensions, extension.TaskList)
}
- if cfg.Extensions.Typographer {
- extensions = append(extensions, extension.Typographer)
+ if !cfg.Extensions.Typographer.Disable {
+ t := extension.NewTypographer(
+ extension.WithTypographicSubstitutions(toTypographicPunctuationMap(cfg.Extensions.Typographer)),
+ )
+ extensions = append(extensions, t)
}
if cfg.Extensions.DefinitionList {
}
return nil
}
+
+// Note: It's tempting to put this in the config package, but that doesn't work.
+// TODO(bep) create upstream issue.
+func toTypographicPunctuationMap(t goldmark_config.Typographer) map[extension.TypographicPunctuation][]byte {
+ return map[extension.TypographicPunctuation][]byte{
+ extension.LeftSingleQuote: []byte(t.LeftSingleQuote),
+ extension.RightSingleQuote: []byte(t.RightSingleQuote),
+ extension.LeftDoubleQuote: []byte(t.LeftDoubleQuote),
+ extension.RightDoubleQuote: []byte(t.RightDoubleQuote),
+ extension.EnDash: []byte(t.EnDash),
+ extension.EmDash: []byte(t.EmDash),
+ extension.Ellipsis: []byte(t.Ellipsis),
+ extension.LeftAngleQuote: []byte(t.LeftAngleQuote),
+ extension.RightAngleQuote: []byte(t.RightAngleQuote),
+ extension.Apostrophe: []byte(t.Apostrophe),
+ }
+
+}
c.Assert(result, qt.Contains, "<span class=\"ln\">2</span><span class=\"cl\">LINE2\n</span></span>")
})
}
+
+func TestTypographerConfig(t *testing.T) {
+ c := qt.New(t)
+
+ content := `
+A "quote" and 'another quote' and a "quote with a 'nested' quote" and a 'quote with a "nested" quote' and an ellipsis...
+`
+ mconf := markup_config.Default
+ mconf.Goldmark.Extensions.Typographer.LeftDoubleQuote = "«"
+ mconf.Goldmark.Extensions.Typographer.RightDoubleQuote = "»"
+ b := convert(c, mconf, content)
+ got := string(b.Bytes())
+
+ c.Assert(got, qt.Contains, "<p>A «quote» and ‘another quote’ and a «quote with a ’nested’ quote» and a ‘quote with a «nested» quote’ and an ellipsis…</p>\n")
+}
// DefaultConfig holds the default Goldmark configuration.
var Default = Config{
Extensions: Extensions{
- Typographer: true,
+ Typographer: Typographer{
+ Disable: false,
+ LeftSingleQuote: "‘",
+ RightSingleQuote: "’",
+ LeftDoubleQuote: "“",
+ RightDoubleQuote: "”",
+ EnDash: "–",
+ EmDash: "—",
+ Ellipsis: "…",
+ LeftAngleQuote: "«",
+ RightAngleQuote: "»",
+ Apostrophe: "’",
+ },
Footnote: true,
DefinitionList: true,
Table: true,
}
type Extensions struct {
- Typographer bool
+ Typographer Typographer
Footnote bool
DefinitionList bool
TaskList bool
}
+// Typographer holds typographer configuration.
+type Typographer struct {
+ // Whether to disable typographer.
+ Disable bool
+
+ // Value used for left single quote.
+ LeftSingleQuote string
+ // Value used for right single quote.
+ RightSingleQuote string
+ // Value used for left double quote.
+ LeftDoubleQuote string
+ // Value used for right double quote.
+ RightDoubleQuote string
+ // Value used for en dash.
+ EnDash string
+ // Value used for em dash.
+ EmDash string
+ // Value used for ellipsis.
+ Ellipsis string
+ // Value used for left angle quote.
+ LeftAngleQuote string
+ // Value used for right angle quote.
+ RightAngleQuote string
+ // Value used for apostrophe.
+ Apostrophe string
+}
+
type Renderer struct {
// Whether softline breaks should be rendered as '<br>'
HardWraps bool
func normalizeConfig(m map[string]any) {
v, err := maps.GetNestedParam("goldmark.parser", ".", m)
- if err != nil {
- return
+ if err == nil {
+ vm := maps.ToStringMap(v)
+ // Changed from a bool in 0.81.0
+ if vv, found := vm["attribute"]; found {
+ if vvb, ok := vv.(bool); ok {
+ vm["attribute"] = goldmark_config.ParserAttribute{
+ Title: vvb,
+ }
+ }
+ }
}
- vm := maps.ToStringMap(v)
- // Changed from a bool in 0.81.0
- if vv, found := vm["attribute"]; found {
- if vvb, ok := vv.(bool); ok {
- vm["attribute"] = goldmark_config.ParserAttribute{
- Title: vvb,
+
+ // Changed from a bool in 0.112.0.
+ v, err = maps.GetNestedParam("goldmark.extensions", ".", m)
+ if err == nil {
+ vm := maps.ToStringMap(v)
+ const typographerKey = "typographer"
+ if vv, found := vm[typographerKey]; found {
+ if vvb, ok := vv.(bool); ok {
+ if !vvb {
+ vm[typographerKey] = goldmark_config.Typographer{
+ Disable: true,
+ }
+ } else {
+ delete(vm, typographerKey)
+ }
}
}
}
c.Assert(conf.AsciidocExt.Extensions[0], qt.Equals, "asciidoctor-html5s")
})
+ c.Run("Decode legacy typographer", func(c *qt.C) {
+ c.Parallel()
+ v := config.New()
+
+ // typographer was changed from a bool to a struct in 0.112.0.
+ v.Set("markup", map[string]any{
+ "goldmark": map[string]any{
+ "extensions": map[string]any{
+ "typographer": false,
+ },
+ },
+ })
+
+ conf, err := Decode(v)
+
+ c.Assert(err, qt.IsNil)
+ c.Assert(conf.Goldmark.Extensions.Typographer.Disable, qt.Equals, true)
+
+ v.Set("markup", map[string]any{
+ "goldmark": map[string]any{
+ "extensions": map[string]any{
+ "typographer": true,
+ },
+ },
+ })
+
+ conf, err = Decode(v)
+
+ c.Assert(err, qt.IsNil)
+ c.Assert(conf.Goldmark.Extensions.Typographer.Disable, qt.Equals, false)
+ c.Assert(conf.Goldmark.Extensions.Typographer.Ellipsis, qt.Equals, "…")
+
+ })
+
}