1package extension
2
3import (
4 "github.com/yuin/goldmark"
5 gast "github.com/yuin/goldmark/ast"
6 "github.com/yuin/goldmark/extension/ast"
7 "github.com/yuin/goldmark/parser"
8 "github.com/yuin/goldmark/renderer"
9 "github.com/yuin/goldmark/renderer/html"
10 "github.com/yuin/goldmark/text"
11 "github.com/yuin/goldmark/util"
12)
13
14type strikethroughDelimiterProcessor struct {
15}
16
17func (p *strikethroughDelimiterProcessor) IsDelimiter(b byte) bool {
18 return b == '~'
19}
20
21func (p *strikethroughDelimiterProcessor) CanOpenCloser(opener, closer *parser.Delimiter) bool {
22 return opener.Char == closer.Char
23}
24
25func (p *strikethroughDelimiterProcessor) OnMatch(consumes int) gast.Node {
26 return ast.NewStrikethrough()
27}
28
29var defaultStrikethroughDelimiterProcessor = &strikethroughDelimiterProcessor{}
30
31type strikethroughParser struct {
32}
33
34var defaultStrikethroughParser = &strikethroughParser{}
35
36// NewStrikethroughParser return a new InlineParser that parses
37// strikethrough expressions.
38func NewStrikethroughParser() parser.InlineParser {
39 return defaultStrikethroughParser
40}
41
42func (s *strikethroughParser) Trigger() []byte {
43 return []byte{'~'}
44}
45
46func (s *strikethroughParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
47 before := block.PrecendingCharacter()
48 line, segment := block.PeekLine()
49 node := parser.ScanDelimiter(line, before, 1, defaultStrikethroughDelimiterProcessor)
50 if node == nil || node.OriginalLength > 2 || before == '~' {
51 return nil
52 }
53
54 node.Segment = segment.WithStop(segment.Start + node.OriginalLength)
55 block.Advance(node.OriginalLength)
56 pc.PushDelimiter(node)
57 return node
58}
59
60func (s *strikethroughParser) CloseBlock(parent gast.Node, pc parser.Context) {
61 // nothing to do
62}
63
64// StrikethroughHTMLRenderer is a renderer.NodeRenderer implementation that
65// renders Strikethrough nodes.
66type StrikethroughHTMLRenderer struct {
67 html.Config
68}
69
70// NewStrikethroughHTMLRenderer returns a new StrikethroughHTMLRenderer.
71func NewStrikethroughHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
72 r := &StrikethroughHTMLRenderer{
73 Config: html.NewConfig(),
74 }
75 for _, opt := range opts {
76 opt.SetHTMLOption(&r.Config)
77 }
78 return r
79}
80
81// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
82func (r *StrikethroughHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
83 reg.Register(ast.KindStrikethrough, r.renderStrikethrough)
84}
85
86// StrikethroughAttributeFilter defines attribute names which dd elements can have.
87var StrikethroughAttributeFilter = html.GlobalAttributeFilter
88
89func (r *StrikethroughHTMLRenderer) renderStrikethrough(
90 w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
91 if entering {
92 if n.Attributes() != nil {
93 _, _ = w.WriteString("<del")
94 html.RenderAttributes(w, n, StrikethroughAttributeFilter)
95 _ = w.WriteByte('>')
96 } else {
97 _, _ = w.WriteString("<del>")
98 }
99 } else {
100 _, _ = w.WriteString("</del>")
101 }
102 return gast.WalkContinue, nil
103}
104
105type strikethrough struct {
106}
107
108// Strikethrough is an extension that allow you to use strikethrough expression like '~~text~~' .
109var Strikethrough = &strikethrough{}
110
111func (e *strikethrough) Extend(m goldmark.Markdown) {
112 m.Parser().AddOptions(parser.WithInlineParsers(
113 util.Prioritized(NewStrikethroughParser(), 500),
114 ))
115 m.Renderer().AddOptions(renderer.WithNodeRenderers(
116 util.Prioritized(NewStrikethroughHTMLRenderer(), 500),
117 ))
118}