From a8f2c7f29bc383a9ace5b1226ab3f6bdc7e74183 Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 21 Apr 2026 10:26:25 +0800 Subject: [PATCH 1/2] fix: require double tildes for strikethrough formatting goldmark's default GFM strikethrough parser accepts both single (~) and double (~~) tildes. Single tildes caused false positives. Replaces the bundled GFM extension with individual extensions plus a custom parser that requires minimum 2 tildes. --- internal/formatter.go | 44 +++++++++++++++++++++++++++++++++++++- internal/formatter_test.go | 5 +++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/internal/formatter.go b/internal/formatter.go index 4eb3621..6fd6958 100644 --- a/internal/formatter.go +++ b/internal/formatter.go @@ -9,12 +9,54 @@ import ( "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/extension" extast "github.com/yuin/goldmark/extension/ast" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/renderer" "github.com/yuin/goldmark/text" + "github.com/yuin/goldmark/util" ) +// strikethroughDelimiter implements parser.DelimiterProcessor for ~~ strikethrough. +type strikethroughDelimiter struct{} + +func (p *strikethroughDelimiter) IsDelimiter(b byte) bool { return b == '~' } +func (p *strikethroughDelimiter) CanOpenCloser(o, c *parser.Delimiter) bool { return o.Char == c.Char } +func (p *strikethroughDelimiter) OnMatch(int) ast.Node { return extast.NewStrikethrough() } + +// doubleStrikethroughParser requires ~~ (not single ~) for strikethrough. +type doubleStrikethroughParser struct{} + +func (s *doubleStrikethroughParser) Trigger() []byte { return []byte{'~'} } + +func (s *doubleStrikethroughParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node { + before := block.PrecendingCharacter() + line, segment := block.PeekLine() + node := parser.ScanDelimiter(line, before, 2, &strikethroughDelimiter{}) + if node == nil || node.OriginalLength > 2 || before == '~' { + return nil + } + node.Segment = segment.WithStop(segment.Start + node.OriginalLength) + block.Advance(node.OriginalLength) + pc.PushDelimiter(node) + return node +} + +func (s *doubleStrikethroughParser) CloseBlock(parent ast.Node, pc parser.Context) {} + var md = goldmark.New( goldmark.WithExtensions( - extension.GFM, + extension.Linkify, + extension.Table, + extension.TaskList, + ), + goldmark.WithParserOptions( + parser.WithInlineParsers( + util.Prioritized(&doubleStrikethroughParser{}, 500), + ), + ), + goldmark.WithRendererOptions( + renderer.WithNodeRenderers( + util.Prioritized(extension.NewStrikethroughHTMLRenderer(), 500), + ), ), ) diff --git a/internal/formatter_test.go b/internal/formatter_test.go index cdd9756..248def8 100644 --- a/internal/formatter_test.go +++ b/internal/formatter_test.go @@ -27,6 +27,11 @@ func TestFormatTelegramHTML(t *testing.T) { input: "~~deleted~~", expected: "deleted", }, + { + name: "single tilde not strikethrough", + input: "~not strikethrough~", + expected: "~not strikethrough~", + }, { name: "inline code", input: "`some code`", From 6849cd071ac5b557282a1abf4b8e2d184a03ca43 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 22 Apr 2026 21:45:55 +0800 Subject: [PATCH 2/2] style: gofmt internal/formatter.go --- internal/formatter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/formatter.go b/internal/formatter.go index 6fd6958..b36099d 100644 --- a/internal/formatter.go +++ b/internal/formatter.go @@ -18,9 +18,9 @@ import ( // strikethroughDelimiter implements parser.DelimiterProcessor for ~~ strikethrough. type strikethroughDelimiter struct{} -func (p *strikethroughDelimiter) IsDelimiter(b byte) bool { return b == '~' } +func (p *strikethroughDelimiter) IsDelimiter(b byte) bool { return b == '~' } func (p *strikethroughDelimiter) CanOpenCloser(o, c *parser.Delimiter) bool { return o.Char == c.Char } -func (p *strikethroughDelimiter) OnMatch(int) ast.Node { return extast.NewStrikethrough() } +func (p *strikethroughDelimiter) OnMatch(int) ast.Node { return extast.NewStrikethrough() } // doubleStrikethroughParser requires ~~ (not single ~) for strikethrough. type doubleStrikethroughParser struct{}