mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
markup/goldmark: Add config options for the typographer extension
Note that the config per language part of this will be handled in #10602. Updates #9772
This commit is contained in:
parent
d01731d53c
commit
5596dc24a0
5 changed files with 139 additions and 12 deletions
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/gohugoio/hugo/identity"
|
"github.com/gohugoio/hugo/identity"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/markup/goldmark/codeblocks"
|
"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/images"
|
||||||
"github.com/gohugoio/hugo/markup/goldmark/internal/extensions/attributes"
|
"github.com/gohugoio/hugo/markup/goldmark/internal/extensions/attributes"
|
||||||
"github.com/gohugoio/hugo/markup/goldmark/internal/render"
|
"github.com/gohugoio/hugo/markup/goldmark/internal/render"
|
||||||
|
@ -120,8 +121,11 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
|
||||||
extensions = append(extensions, extension.TaskList)
|
extensions = append(extensions, extension.TaskList)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Extensions.Typographer {
|
if !cfg.Extensions.Typographer.Disable {
|
||||||
extensions = append(extensions, extension.Typographer)
|
t := extension.NewTypographer(
|
||||||
|
extension.WithTypographicSubstitutions(toTypographicPunctuationMap(cfg.Extensions.Typographer)),
|
||||||
|
)
|
||||||
|
extensions = append(extensions, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Extensions.DefinitionList {
|
if cfg.Extensions.DefinitionList {
|
||||||
|
@ -278,3 +282,21 @@ func (p *parserContext) TableOfContents() *tableofcontents.Fragments {
|
||||||
}
|
}
|
||||||
return nil
|
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),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -499,3 +499,18 @@ LINE5
|
||||||
c.Assert(result, qt.Contains, "<span class=\"ln\">2</span><span class=\"cl\">LINE2\n</span></span>")
|
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")
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,19 @@ const (
|
||||||
// DefaultConfig holds the default Goldmark configuration.
|
// DefaultConfig holds the default Goldmark configuration.
|
||||||
var Default = Config{
|
var Default = Config{
|
||||||
Extensions: Extensions{
|
Extensions: Extensions{
|
||||||
Typographer: true,
|
Typographer: Typographer{
|
||||||
|
Disable: false,
|
||||||
|
LeftSingleQuote: "‘",
|
||||||
|
RightSingleQuote: "’",
|
||||||
|
LeftDoubleQuote: "“",
|
||||||
|
RightDoubleQuote: "”",
|
||||||
|
EnDash: "–",
|
||||||
|
EmDash: "—",
|
||||||
|
Ellipsis: "…",
|
||||||
|
LeftAngleQuote: "«",
|
||||||
|
RightAngleQuote: "»",
|
||||||
|
Apostrophe: "’",
|
||||||
|
},
|
||||||
Footnote: true,
|
Footnote: true,
|
||||||
DefinitionList: true,
|
DefinitionList: true,
|
||||||
Table: true,
|
Table: true,
|
||||||
|
@ -54,7 +66,7 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Extensions struct {
|
type Extensions struct {
|
||||||
Typographer bool
|
Typographer Typographer
|
||||||
Footnote bool
|
Footnote bool
|
||||||
DefinitionList bool
|
DefinitionList bool
|
||||||
|
|
||||||
|
@ -66,6 +78,33 @@ type Extensions struct {
|
||||||
TaskList 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 {
|
type Renderer struct {
|
||||||
// Whether softline breaks should be rendered as '<br>'
|
// Whether softline breaks should be rendered as '<br>'
|
||||||
HardWraps bool
|
HardWraps bool
|
||||||
|
|
|
@ -62,9 +62,7 @@ func Decode(cfg config.Provider) (conf Config, err error) {
|
||||||
|
|
||||||
func normalizeConfig(m map[string]any) {
|
func normalizeConfig(m map[string]any) {
|
||||||
v, err := maps.GetNestedParam("goldmark.parser", ".", m)
|
v, err := maps.GetNestedParam("goldmark.parser", ".", m)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return
|
|
||||||
}
|
|
||||||
vm := maps.ToStringMap(v)
|
vm := maps.ToStringMap(v)
|
||||||
// Changed from a bool in 0.81.0
|
// Changed from a bool in 0.81.0
|
||||||
if vv, found := vm["attribute"]; found {
|
if vv, found := vm["attribute"]; found {
|
||||||
|
@ -74,6 +72,25 @@ func normalizeConfig(m map[string]any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var Default = Config{
|
var Default = Config{
|
||||||
|
|
|
@ -52,4 +52,38 @@ func TestConfig(t *testing.T) {
|
||||||
c.Assert(conf.AsciidocExt.Extensions[0], qt.Equals, "asciidoctor-html5s")
|
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, "…")
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue