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, 2, defaultStrikethroughDelimiterProcessor)
 50	if node == nil {
 51		return nil
 52	}
 53	node.Segment = segment.WithStop(segment.Start + node.OriginalLength)
 54	block.Advance(node.OriginalLength)
 55	pc.PushDelimiter(node)
 56	return node
 57}
 58
 59func (s *strikethroughParser) CloseBlock(parent gast.Node, pc parser.Context) {
 60	// nothing to do
 61}
 62
 63// StrikethroughHTMLRenderer is a renderer.NodeRenderer implementation that
 64// renders Strikethrough nodes.
 65type StrikethroughHTMLRenderer struct {
 66	html.Config
 67}
 68
 69// NewStrikethroughHTMLRenderer returns a new StrikethroughHTMLRenderer.
 70func NewStrikethroughHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
 71	r := &StrikethroughHTMLRenderer{
 72		Config: html.NewConfig(),
 73	}
 74	for _, opt := range opts {
 75		opt.SetHTMLOption(&r.Config)
 76	}
 77	return r
 78}
 79
 80// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
 81func (r *StrikethroughHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
 82	reg.Register(ast.KindStrikethrough, r.renderStrikethrough)
 83}
 84
 85// StrikethroughAttributeFilter defines attribute names which dd elements can have.
 86var StrikethroughAttributeFilter = html.GlobalAttributeFilter
 87
 88func (r *StrikethroughHTMLRenderer) renderStrikethrough(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
 89	if entering {
 90		if n.Attributes() != nil {
 91			_, _ = w.WriteString("<del")
 92			html.RenderAttributes(w, n, StrikethroughAttributeFilter)
 93			_ = w.WriteByte('>')
 94		} else {
 95			_, _ = w.WriteString("<del>")
 96		}
 97	} else {
 98		_, _ = w.WriteString("</del>")
 99	}
100	return gast.WalkContinue, nil
101}
102
103type strikethrough struct {
104}
105
106// Strikethrough is an extension that allow you to use strikethrough expression like '~~text~~' .
107var Strikethrough = &strikethrough{}
108
109func (e *strikethrough) Extend(m goldmark.Markdown) {
110	m.Parser().AddOptions(parser.WithInlineParsers(
111		util.Prioritized(NewStrikethroughParser(), 500),
112	))
113	m.Renderer().AddOptions(renderer.WithNodeRenderers(
114		util.Prioritized(NewStrikethroughHTMLRenderer(), 500),
115	))
116}