From 9d0b1b6bb83ef4abe51755dcc3f46a6930084288 Mon Sep 17 00:00:00 2001 From: OMOTO Tsukasa Date: Sat, 23 Sep 2023 20:49:54 +0900 Subject: [PATCH] Define `EastAsianLineBreaksStyle` to specify behavior of line breaking --- extension/cjk.go | 42 ++++++++++---------- extension/cjk_test.go | 4 +- renderer/html/html.go | 89 +++++++++++++++++++++++++------------------ 3 files changed, 75 insertions(+), 60 deletions(-) diff --git a/extension/cjk.go b/extension/cjk.go index b1e5dcd..9cf5381 100644 --- a/extension/cjk.go +++ b/extension/cjk.go @@ -9,31 +9,31 @@ import ( // A CJKOption sets options for CJK support mostly for HTML based renderers. type CJKOption func(*cjk) -// A EastAsianLineBreaksOption sets options for east asian line breaks. -type EastAsianLineBreaksOption func(*eastAsianLineBreaks) +// A EastAsianLineBreaksStyle is a style of east asian line breaks. +type EastAsianLineBreaksStyle int + +const ( + EastAsianLineBreaksStyleSimple EastAsianLineBreaksStyle = iota + EastAsianLineBreaksCSS3Draft +) + +type EastAsianLineBreaksFunction func() // WithEastAsianLineBreaks is a functional option that indicates whether softline breaks // between east asian wide characters should be ignored. -func WithEastAsianLineBreaks(opts ...EastAsianLineBreaksOption) CJKOption { +func WithEastAsianLineBreaks(style ...EastAsianLineBreaksStyle) CJKOption { return func(c *cjk) { e := &eastAsianLineBreaks{ - Enabled: true, + Enabled: true, + EastAsianLineBreaksStyle: EastAsianLineBreaksStyleSimple, } - for _, opt := range opts { - opt(e) + for _, s := range style { + e.EastAsianLineBreaksStyle = s } c.EastAsianLineBreaks = e } } -// WithWorksEvenWithOneSide is a functional option that indicates that a softline break -// is ignored even if only one side of the break is east asian wide character. -func WithWorksEvenWithOneSide() EastAsianLineBreaksOption { - return func(e *eastAsianLineBreaks) { - e.WorksEvenWithOneSide = true - } -} - // WithEscapedSpace is a functional option that indicates that a '\' escaped half-space(0x20) should not be rendered. func WithEscapedSpace() CJKOption { return func(c *cjk) { @@ -47,8 +47,8 @@ type cjk struct { } type eastAsianLineBreaks struct { - Enabled bool - WorksEvenWithOneSide bool + Enabled bool + EastAsianLineBreaksStyle EastAsianLineBreaksStyle } // CJK is a goldmark extension that provides functionalities for CJK languages. @@ -66,13 +66,13 @@ func NewCJK(opts ...CJKOption) goldmark.Extender { func (e *cjk) Extend(m goldmark.Markdown) { if e.EastAsianLineBreaks != nil { if e.EastAsianLineBreaks.Enabled { - opts := []html.EastAsianLineBreaksOption{} - if e.EastAsianLineBreaks.WorksEvenWithOneSide { - opts = append(opts, html.WithWorksEvenWithOneSide()) + style := html.EastAsianLineBreaksStyleSimple + switch e.EastAsianLineBreaks.EastAsianLineBreaksStyle { + case EastAsianLineBreaksCSS3Draft: + style = html.EastAsianLineBreaksCSS3Draft } - m.Renderer().AddOptions(html.WithEastAsianLineBreaks(opts...)) + m.Renderer().AddOptions(html.WithEastAsianLineBreaks(style)) } - } if e.EscapedSpace { m.Renderer().AddOptions(html.WithWriter(html.NewWriter(html.WithEscapedSpace()))) diff --git a/extension/cjk_test.go b/extension/cjk_test.go index e4336f3..7091284 100644 --- a/extension/cjk_test.go +++ b/extension/cjk_test.go @@ -209,13 +209,13 @@ func TestEastAsianLineBreaks(t *testing.T) { t, ) - // WithWorksEvenWithOneSide option + // test with EastAsianLineBreaksCSS3Draft markdown = goldmark.New(goldmark.WithRendererOptions( html.WithXHTML(), html.WithUnsafe(), ), goldmark.WithExtensions( - NewCJK(WithEastAsianLineBreaks(WithWorksEvenWithOneSide())), + NewCJK(WithEastAsianLineBreaks(EastAsianLineBreaksCSS3Draft)), ), ) no = 9 diff --git a/renderer/html/html.go b/renderer/html/html.go index 0ca7c52..e2629cc 100644 --- a/renderer/html/html.go +++ b/renderer/html/html.go @@ -103,52 +103,75 @@ func WithHardWraps() interface { // EastAsianLineBreaks is an option name used in WithEastAsianLineBreaks. const optEastAsianLineBreaks renderer.OptionName = "EastAsianLineBreaks" +type EastAsianLineBreaksStyle int + +const ( + EastAsianLineBreaksStyleSimple EastAsianLineBreaksStyle = iota + EastAsianLineBreaksCSS3Draft +) + +type eastAsianLineBreaksFunction interface { + SoftLineBreak(thisLastRune rune, siblingFirstRune rune) bool +} + +type eastAsianLineBreaksSimple struct{} + +func (e *eastAsianLineBreaksSimple) SoftLineBreak(thisLastRune rune, siblingFirstRune rune) bool { + return !(util.IsEastAsianWideRune(thisLastRune) && util.IsEastAsianWideRune(siblingFirstRune)) +} + +type eastAsianLineBreaksCSS3Draft struct{} + +func (e *eastAsianLineBreaksCSS3Draft) SoftLineBreak(thisLastRune rune, siblingFirstRune rune) bool { + return !(util.IsEastAsianWideRune(thisLastRune) || util.IsEastAsianWideRune(siblingFirstRune)) +} + type eastAsianLineBreaks struct { - Enabled bool - WorksEvenWithOneSide bool + Enabled bool + EastAsianLineBreaksFunction eastAsianLineBreaksFunction } type withEastAsianLineBreaks struct { - worksEvenWithOneSide bool + eastAsianLineBreaksStyle EastAsianLineBreaksStyle } -// A EastAsianLineBreaksOption sets options for east asian line breaks. -type EastAsianLineBreaksOption func(*withEastAsianLineBreaks) - func (o *withEastAsianLineBreaks) SetConfig(c *renderer.Config) { - c.Options[optEastAsianLineBreaks] = eastAsianLineBreaks{ - Enabled: true, - WorksEvenWithOneSide: o.worksEvenWithOneSide, + switch o.eastAsianLineBreaksStyle { + case EastAsianLineBreaksStyleSimple: + c.Options[optEastAsianLineBreaks] = eastAsianLineBreaks{ + Enabled: true, + EastAsianLineBreaksFunction: &eastAsianLineBreaksSimple{}, + } + case EastAsianLineBreaksCSS3Draft: + c.Options[optEastAsianLineBreaks] = eastAsianLineBreaks{ + Enabled: true, + EastAsianLineBreaksFunction: &eastAsianLineBreaksCSS3Draft{}, + } } } func (o *withEastAsianLineBreaks) SetHTMLOption(c *Config) { - c.EastAsianLineBreaks = eastAsianLineBreaks{ - Enabled: true, - WorksEvenWithOneSide: o.worksEvenWithOneSide, + switch o.eastAsianLineBreaksStyle { + case EastAsianLineBreaksStyleSimple: + c.EastAsianLineBreaks = eastAsianLineBreaks{ + Enabled: true, + EastAsianLineBreaksFunction: &eastAsianLineBreaksSimple{}, + } + case EastAsianLineBreaksCSS3Draft: + c.EastAsianLineBreaks = eastAsianLineBreaks{ + Enabled: true, + EastAsianLineBreaksFunction: &eastAsianLineBreaksCSS3Draft{}, + } } } // WithEastAsianLineBreaks is a functional option that indicates whether softline breaks // between east asian wide characters should be ignored. -func WithEastAsianLineBreaks(opts ...EastAsianLineBreaksOption) interface { +func WithEastAsianLineBreaks(style EastAsianLineBreaksStyle) interface { renderer.Option Option } { - w := &withEastAsianLineBreaks{} - for _, opt := range opts { - opt(w) - } - - return w -} - -// WithWorksEvenWithOneSide is a functional option that indicates that a softline break -// is ignored even if only one side of the break is east asian wide character. -func WithWorksEvenWithOneSide() EastAsianLineBreaksOption { - return func(o *withEastAsianLineBreaks) { - o.worksEvenWithOneSide = true - } + return &withEastAsianLineBreaks{style} } // XHTML is an option name used in WithXHTML. @@ -697,16 +720,8 @@ func (r *Renderer) renderText(w util.BufWriter, source []byte, node ast.Node, en if siblingText := sibling.(*ast.Text).Text(source); len(siblingText) != 0 { thisLastRune := util.ToRune(value, len(value)-1) siblingFirstRune, _ := utf8.DecodeRune(siblingText) - if r.EastAsianLineBreaks.WorksEvenWithOneSide { - if !(util.IsEastAsianWideRune(thisLastRune) || - util.IsEastAsianWideRune(siblingFirstRune)) { - _ = w.WriteByte('\n') - } - } else { - if !(util.IsEastAsianWideRune(thisLastRune) && - util.IsEastAsianWideRune(siblingFirstRune)) { - _ = w.WriteByte('\n') - } + if r.EastAsianLineBreaks.EastAsianLineBreaksFunction.SoftLineBreak(thisLastRune, siblingFirstRune) { + _ = w.WriteByte('\n') } } }