Skip to content

Commit 308ec73

Browse files
committed
markup/goldmark: Add goldmark-cjk-friendly extension
Add `goldmark-cjk-friendly` extension which enhances handling of CJK emphasis and strikethrough. Fixes #14114
1 parent ff0f67e commit 308ec73

File tree

6 files changed

+155
-2
lines changed

6 files changed

+155
-2
lines changed

‎docs/data/docs.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,7 @@ config:
12261226
eastAsianLineBreaksStyle: simple
12271227
enable: false
12281228
escapedSpace: false
1229+
friendlyEmphasis: false
12291230
definitionList: true
12301231
extras:
12311232
delete:

‎go.mod‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ require (
6868
github.com/spf13/cobra v1.9.1
6969
github.com/spf13/fsync v0.10.1
7070
github.com/spf13/pflag v1.0.7
71+
github.com/tats-u/goldmark-cjk-friendly v1.0.0
7172
github.com/tdewolff/minify/v2 v2.24.5
7273
github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a
7374
github.com/tetratelabs/wazero v1.9.0
@@ -192,4 +193,4 @@ require (
192193
software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect
193194
)
194195

195-
go 1.24.0
196+
go 1.25.1

‎go.sum‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
513513
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
514514
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
515515
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
516+
github.com/tats-u/goldmark-cjk-friendly v1.0.0 h1:CV9DuvaobrxOEII9m3MxW3rGkRQcBuWHud4tJrfoWuk=
517+
github.com/tats-u/goldmark-cjk-friendly v1.0.0/go.mod h1:gkL4MS2BRA7T5JRDLHs7PRij0/boXtbxas6DG3Yyedk=
516518
github.com/tdewolff/minify/v2 v2.24.5 h1:ytxthX3xSxrK3Xx5B38flg5moCKs/dB8VwiD/RzJViU=
517519
github.com/tdewolff/minify/v2 v2.24.5/go.mod h1:q09KtNnVai7TyEzGEZeWPAnK+c8Z+NI8prCXZW652bo=
518520
github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a h1:Rmq+utdraciok/97XHRweYdsAo/M4LOswpCboo3yvN4=

‎markup/goldmark/convert.go‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/gohugoio/hugo/markup/goldmark/tables"
3030
"github.com/yuin/goldmark/util"
3131

32+
cjkfriendly "github.com/tats-u/goldmark-cjk-friendly"
3233
"github.com/yuin/goldmark"
3334
emoji "github.com/yuin/goldmark-emoji"
3435
"github.com/yuin/goldmark/ast"
@@ -153,7 +154,7 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
153154
extensions = append(extensions, tables.New())
154155
}
155156

156-
if cfg.Extensions.Strikethrough {
157+
if cfg.Extensions.Strikethrough && !cfg.Extensions.CJK.Enable && !cfg.Extensions.CJK.FriendlyEmphasis {
157158
extensions = append(extensions, extension.Strikethrough)
158159
}
159160

@@ -205,6 +206,16 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
205206
}
206207
c := extension.NewCJK(opts...)
207208
extensions = append(extensions, c)
209+
210+
if cfg.Extensions.CJK.FriendlyEmphasis {
211+
if cfg.Extensions.Strikethrough {
212+
extensions = append(extensions, cjkfriendly.CJKFriendlyEmphasisAndStrikethrough)
213+
} else {
214+
extensions = append(extensions, cjkfriendly.CJKFriendlyEmphasis)
215+
}
216+
} else if cfg.Extensions.Strikethrough {
217+
extensions = append(extensions, cjkfriendly.CJKFriendlyStrikethrough)
218+
}
208219
}
209220

210221
if cfg.Extensions.Passthrough.Enable {

‎markup/goldmark/convert_test.go‎

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,140 @@ escapedSpace=true
746746
c.Assert(got, qt.Contains, "<p>私は太郎です。\nプログラミングが好きです。運動が苦手です。</p>\n")
747747
}
748748

749+
func TestConvertCJKWithExtensionFriendlyEmphasisParametrized(t *testing.T) {
750+
c := qt.New(t)
751+
752+
content := "Git **(ギット)**Hub\n~~(真美好)~~"
753+
754+
tests := []struct {
755+
name string
756+
strikethrough bool
757+
friendlyEmphasis bool
758+
expect string
759+
}{
760+
{"noFriendly_noStrike", false, false, "<p>Git **(ギット)**Hub\n~~(真美好)~~</p>\n"},
761+
{"friendly_noStrike", false, true, "<p>Git <strong>(ギット)</strong>Hub\n~~(真美好)~~</p>\n"},
762+
{"noFriendly_strike", true, false, "<p>Git **(ギット)**Hub\n<del>(真美好)</del></p>\n"},
763+
{"friendly_strike", true, true, "<p>Git <strong>(ギット)</strong>Hub\n<del>(真美好)</del></p>\n"},
764+
}
765+
766+
for _, tt := range tests {
767+
c.Run(tt.name, func(c *qt.C) {
768+
confStr := fmt.Sprintf(`
769+
[markup]
770+
[markup.goldmark]
771+
[markup.goldmark.extensions]
772+
strikethrough=%v
773+
[markup.goldmark.extensions.CJK]
774+
enable=true
775+
friendlyEmphasis=%v
776+
`, tt.strikethrough, tt.friendlyEmphasis)
777+
778+
cfg := config.FromTOMLConfigString(confStr)
779+
conf := testconfig.GetTestConfig(nil, cfg)
780+
781+
b := convert(c, conf, content)
782+
got := string(b.Bytes())
783+
784+
c.Assert(got, qt.Contains, tt.expect)
785+
})
786+
}
787+
}
788+
789+
func TestConvertCJKWithExtensionFriendlyEmphasisTableDriven(t *testing.T) {
790+
c := qt.New(t)
791+
792+
content := "a\\ **()**\\ a𩸽**()**𩸽~~(真美好)~~"
793+
794+
tests := []struct {
795+
name string
796+
strikethrough bool
797+
escapedSpace bool
798+
friendlyEmphasis bool
799+
expect string
800+
}{
801+
{
802+
name: "noFriendly_noStrike_noEscaped",
803+
strikethrough: false,
804+
escapedSpace: false,
805+
friendlyEmphasis: false,
806+
expect: "<p>a\\ <strong>()</strong>\\ a𩸽**()**𩸽~~(真美好)~~</p>\n",
807+
},
808+
{
809+
name: "noFriendly_noStrike_escaped",
810+
strikethrough: false,
811+
escapedSpace: true,
812+
friendlyEmphasis: false,
813+
expect: "<p>a<strong>()</strong>a𩸽**()**𩸽~~(真美好)~~</p>\n",
814+
},
815+
{
816+
name: "noFriendly_strike_noEscaped",
817+
strikethrough: true,
818+
escapedSpace: false,
819+
friendlyEmphasis: false,
820+
expect: "<p>a\\ <strong>()</strong>\\ a𩸽**()**𩸽<del>(真美好)</del></p>\n",
821+
},
822+
{
823+
name: "noFriendly_strike_escaped",
824+
strikethrough: true,
825+
escapedSpace: true,
826+
friendlyEmphasis: false,
827+
expect: "<p>a<strong>()</strong>a𩸽**()**𩸽<del>(真美好)</del></p>\n",
828+
},
829+
{
830+
name: "friendly_noStrike_noEscaped",
831+
strikethrough: false,
832+
escapedSpace: false,
833+
friendlyEmphasis: true,
834+
expect: "<p>a\\ <strong>()</strong>\\ a𩸽<strong>()</strong>𩸽~~(真美好)~~</p>\n",
835+
},
836+
{
837+
name: "friendly_noStrike_escaped",
838+
strikethrough: false,
839+
escapedSpace: true,
840+
friendlyEmphasis: true,
841+
expect: "<p>a<strong>()</strong>a𩸽<strong>()</strong>𩸽~~(真美好)~~</p>\n",
842+
},
843+
{
844+
name: "friendly_strike_noEscaped",
845+
strikethrough: true,
846+
escapedSpace: false,
847+
friendlyEmphasis: true,
848+
expect: "<p>a\\ <strong>()</strong>\\ a𩸽<strong>()</strong>𩸽<del>(真美好)</del></p>\n",
849+
},
850+
{
851+
name: "friendly_strike_escaped",
852+
strikethrough: true,
853+
escapedSpace: true,
854+
friendlyEmphasis: true,
855+
expect: "<p>a<strong>()</strong>a𩸽<strong>()</strong>𩸽<del>(真美好)</del></p>\n",
856+
},
857+
}
858+
859+
for _, tt := range tests {
860+
c.Run(tt.name, func(c *qt.C) {
861+
confStr := fmt.Sprintf(`
862+
[markup]
863+
[markup.goldmark]
864+
[markup.goldmark.extensions]
865+
strikethrough=%v
866+
[markup.goldmark.extensions.CJK]
867+
enable=true
868+
escapedSpace=%v
869+
friendlyEmphasis=%v
870+
`, tt.strikethrough, tt.escapedSpace, tt.friendlyEmphasis)
871+
872+
cfg := config.FromTOMLConfigString(confStr)
873+
conf := testconfig.GetTestConfig(nil, cfg)
874+
875+
b := convert(c, conf, content)
876+
got := string(b.Bytes())
877+
878+
c.Assert(got, qt.Contains, tt.expect)
879+
})
880+
}
881+
}
882+
749883
type tableRenderer int
750884

751885
func (hr tableRenderer) RenderTable(cctx context.Context, w hugio.FlexiWriter, ctx hooks.TableContext) error {

‎markup/goldmark/goldmark_config/config.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ var Default = Config{
5656
EastAsianLineBreaks: false,
5757
EastAsianLineBreaksStyle: "simple",
5858
EscapedSpace: false,
59+
FriendlyEmphasis: false,
5960
},
6061
Extras: Extras{
6162
Delete: Delete{
@@ -268,6 +269,9 @@ type CJK struct {
268269

269270
// Whether a '\' escaped half-space(0x20) should not be rendered.
270271
EscapedSpace bool
272+
273+
// FriendlyEmphasis adds support for CJK-friendly 'emphasis'. If "strikethrough" goldmark extension is enabled as well, CJK-friendly 'emphasis and strikethrough' will be used.
274+
FriendlyEmphasis bool
271275
}
272276

273277
type Renderer struct {

0 commit comments

Comments
 (0)