Repository: gohugoio/hugo (language: go)

Issue:
Add goldmark's CJK extension support
When blackfriday was the default markdown renderer, we could enable the `joinLines` extension (introduced in #3574) to use CJK-style line breaks.

This feature was removed when the renderer was changed to goldmark, but goldmark recently added a [CJK extension](https://github.com/yuin/goldmark#cjk-extension) that enables CJK-style line breaks. It would be very helpful for CJK users if this extension can be used from hugo.


Discussion / hints:

Fix patch:
diff --git a/docs/data/docs.yaml b/docs/data/docs.yaml
index 545ec74fbb6..900c668ca69 100644
--- a/docs/data/docs.yaml
+++ b/docs/data/docs.yaml
@@ -1037,6 +1037,9 @@ config:
           rightAngleQuote: '&raquo;'
           rightDoubleQuote: '&rdquo;'
           rightSingleQuote: '&rsquo;'
+        CJK:
+          eastAsianLineBreaks: false
+          escapedSpace: false
       parser:
         attribute:
           block: false
diff --git a/markup/goldmark/convert.go b/markup/goldmark/convert.go
index 20bbfc2107b..fa2ab548b27 100644
--- a/markup/goldmark/convert.go
+++ b/markup/goldmark/convert.go
@@ -136,6 +136,19 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
 		extensions = append(extensions, extension.Footnote)
 	}
 
+	if cfg.Extensions.CJK.Enable {
+		opts := []extension.CJKOption{}
+		if cfg.Extensions.CJK.EastAsianLineBreaks {
+			opts = append(opts, extension.WithEastAsianLineBreaks())
+		}
+
+		if cfg.Extensions.CJK.EscapedSpace {
+			opts = append(opts, extension.WithEscapedSpace())
+		}
+		c := extension.NewCJK(opts...)
+		extensions = append(extensions, c)
+	}
+
 	if cfg.Parser.AutoHeadingID {
 		parserOptions = append(parserOptions, parser.WithAutoHeadingID())
 	}
diff --git a/markup/goldmark/goldmark_config/config.go b/markup/goldmark/goldmark_config/config.go
index a6fe0e62413..dfbcc5a90bc 100644
--- a/markup/goldmark/goldmark_config/config.go
+++ b/markup/goldmark/goldmark_config/config.go
@@ -43,6 +43,11 @@ var Default = Config{
 		Linkify:         true,
 		LinkifyProtocol: "https",
 		TaskList:        true,
+		CJK: CJK{
+			Enable:              false,
+			EastAsianLineBreaks: false,
+			EscapedSpace:        false,
+		},
 	},
 	Renderer: Renderer{
 		Unsafe: false,
@@ -76,6 +81,7 @@ type Extensions struct {
 	Linkify         bool
 	LinkifyProtocol string
 	TaskList        bool
+	CJK             CJK
 }
 
 // Typographer holds typographer configuration.
@@ -105,6 +111,17 @@ type Typographer struct {
 	Apostrophe string
 }
 
+type CJK struct {
+	// Whether to enable CJK support.
+	Enable bool
+
+	// Whether softline breaks between east asian wide characters should be ignored.
+	EastAsianLineBreaks bool
+
+	// Whether a '\' escaped half-space(0x20) should not be rendered.
+	EscapedSpace bool
+}
+
 type Renderer struct {
 	// Whether softline breaks should be rendered as '<br>'
 	HardWraps bool


Test patch:
diff --git a/markup/goldmark/convert_test.go b/markup/goldmark/convert_test.go
index a2f7b959776..ed791448b93 100644
--- a/markup/goldmark/convert_test.go
+++ b/markup/goldmark/convert_test.go
@@ -626,3 +626,76 @@ unsafe = false
 	return testconfig.GetTestConfig(nil, cfg)
 
 }
+
+func TestConvertCJK(t *testing.T) {
+	c := qt.New(t)
+
+	content := `
+私は太郎です。
+プログラミングが好きです。\ 運動が苦手です。
+`
+
+	confStr := `
+[markup]
+[markup.goldmark]
+`
+
+	cfg := config.FromTOMLConfigString(confStr)
+	conf := testconfig.GetTestConfig(nil, cfg)
+
+	b := convert(c, conf, content)
+	got := string(b.Bytes())
+
+	c.Assert(got, qt.Contains, "<p>私は太郎です。\nプログラミングが好きです。\\ 運動が苦手です。</p>\n")
+}
+
+func TestConvertCJKWithExtensionWithEastAsianLineBreaksOption(t *testing.T) {
+	c := qt.New(t)
+
+	content := `
+私は太郎です。
+プログラミングが好きで、
+運動が苦手です。
+`
+
+	confStr := `
+[markup]
+[markup.goldmark]
+[markup.goldmark.extensions.CJK]
+enable=true
+eastAsianLineBreaks=true
+`
+
+	cfg := config.FromTOMLConfigString(confStr)
+	conf := testconfig.GetTestConfig(nil, cfg)
+
+	b := convert(c, conf, content)
+	got := string(b.Bytes())
+
+	c.Assert(got, qt.Contains, "<p>私は太郎です。プログラミングが好きで、運動が苦手です。</p>\n")
+}
+
+func TestConvertCJKWithExtensionWithEscapedSpaceOption(t *testing.T) {
+	c := qt.New(t)
+
+	content := `
+私は太郎です。
+プログラミングが好きです。\ 運動が苦手です。
+`
+
+	confStr := `
+[markup]
+[markup.goldmark]
+[markup.goldmark.extensions.CJK]
+enable=true
+escapedSpace=true
+`
+
+	cfg := config.FromTOMLConfigString(confStr)
+	conf := testconfig.GetTestConfig(nil, cfg)
+
+	b := convert(c, conf, content)
+	got := string(b.Bytes())
+
+	c.Assert(got, qt.Contains, "<p>私は太郎です。\nプログラミングが好きです。運動が苦手です。</p>\n")
+}