diff --git a/extension/cjk.go b/extension/cjk.go index 60e07ff..cb6f955 100644 --- a/extension/cjk.go +++ b/extension/cjk.go @@ -2,6 +2,7 @@ package extension import ( "github.com/yuin/goldmark" + "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/renderer/html" ) @@ -45,5 +46,6 @@ func (e *cjk) Extend(m goldmark.Markdown) { } if e.EscapedSpace { m.Renderer().AddOptions(html.WithWriter(html.NewWriter(html.WithEscapedSpace()))) + m.Parser().AddOptions(parser.WithEscapedSpace()) } } diff --git a/extension/cjk_test.go b/extension/cjk_test.go index 8863a10..1903ff1 100644 --- a/extension/cjk_test.go +++ b/extension/cjk_test.go @@ -56,6 +56,31 @@ func TestEscapedSpace(t *testing.T) { }, t, ) + + // ' ' triggers Linkify extension inline parser. + // Escaped spaces should not trigger the inline parser. + + markdown = goldmark.New(goldmark.WithRendererOptions( + html.WithXHTML(), + html.WithUnsafe(), + ), + goldmark.WithExtensions( + NewCJK(WithEscapedSpace()), + Linkify, + ), + ) + + no = 4 + testutil.DoTestCase( + markdown, + testutil.MarkdownTestCase{ + No: no, + Description: "Escaped space and linkfy extension", + Markdown: "太郎は\\ **「こんにちわ」**\\ と言った\nんです", + Expected: "

太郎は「こんにちわ」と言った\nんです

", + }, + t, + ) } func TestEastAsianLineBreaks(t *testing.T) { diff --git a/parser/parser.go b/parser/parser.go index bac0704..f41e8a2 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -430,6 +430,7 @@ type Config struct { InlineParsers util.PrioritizedSlice /**/ ParagraphTransformers util.PrioritizedSlice /**/ ASTTransformers util.PrioritizedSlice /**/ + EscapedSpace bool } // NewConfig returns a new Config. @@ -635,6 +636,7 @@ type parser struct { closeBlockers []CloseBlocker paragraphTransformers []ParagraphTransformer astTransformers []ASTTransformer + escapedSpace bool config *Config initSync sync.Once } @@ -695,6 +697,18 @@ func WithASTTransformers(ps ...util.PrioritizedValue) Option { return &withASTTransformers{ps} } +type withEscapedSpace struct { +} + +func (o *withEscapedSpace) SetParserOption(c *Config) { + c.EscapedSpace = true +} + +// WithEscapedSpace is a functional option indicates that a '\' escaped half-space(0x20) should not trigger parsers. +func WithEscapedSpace() Option { + return &withEscapedSpace{} +} + type withOption struct { name OptionName value interface{} @@ -846,6 +860,7 @@ func (p *parser) Parse(reader text.Reader, opts ...ParseOption) ast.Node { for _, v := range p.config.ASTTransformers { p.addASTTransformer(v, p.config.Options) } + p.escapedSpace = p.config.EscapedSpace p.config = nil }) c := &ParseConfig{} @@ -1165,7 +1180,7 @@ func (p *parser) parseBlock(block text.BlockReader, parent ast.Node, pc Context) } isSpace := util.IsSpace(c) isPunct := util.IsPunct(c) - if (isPunct && !escaped) || isSpace || i == 0 { + if (isPunct && !escaped) || isSpace && !(escaped && p.escapedSpace) || i == 0 { parserChar := c if isSpace || (i == 0 && !isPunct) { parserChar = ' '