From 1a4509a3e8447a9f513d58db87c83bfde32a2880 Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Sat, 22 May 2021 18:14:10 +0200 Subject: [PATCH] First implementation --- LICENSE | 2 +- README.md | 4 +- _test/mark.txt | 18 ++++++++ ast/mark.go | 29 +++++++++++++ go.mod | 5 +++ go.sum | 2 + mark.go | 116 +++++++++++++++++++++++++++++++++++++++++++++++++ mark_test.go | 21 +++++++++ 8 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 _test/mark.txt create mode 100644 ast/mark.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 mark.go create mode 100644 mark_test.go diff --git a/LICENSE b/LICENSE index 2071b23..73a5796 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) +Copyright (c) 2021 Jan-Lukas Else Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index e7289f1..112a97c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # goldmark-mark -Goldmark extension for HTML mark tags \ No newline at end of file +Goldmark extension for HTML mark tags + +This is heavily based on the strikethrough extension of Goldmark. \ No newline at end of file diff --git a/_test/mark.txt b/_test/mark.txt new file mode 100644 index 0000000..1b14910 --- /dev/null +++ b/_test/mark.txt @@ -0,0 +1,18 @@ +1 +//- - - - - - - - -// +==Hi== Hello, world! +//- - - - - - - - -// +

Hi Hello, world!

+//= = = = = = = = = = = = = = = = = = = = = = = =// + + + +2 +//- - - - - - - - -// +This ==has a + +new paragraph==. +//- - - - - - - - -// +

This ==has a

+

new paragraph==.

+//= = = = = = = = = = = = = = = = = = = = = = = =// \ No newline at end of file diff --git a/ast/mark.go b/ast/mark.go new file mode 100644 index 0000000..db2ae94 --- /dev/null +++ b/ast/mark.go @@ -0,0 +1,29 @@ +// Package ast defines AST nodes that represents extension's elements +package ast + +import ( + gast "github.com/yuin/goldmark/ast" +) + +// A Mark struct represents a strikethrough of GFM text. +type Mark struct { + gast.BaseInline +} + +// Dump implements Node.Dump. +func (n *Mark) Dump(source []byte, level int) { + gast.DumpHelper(n, source, level, nil, nil) +} + +// KindMark is a NodeKind of the Strikethrough node. +var KindMark = gast.NewNodeKind("Mark") + +// Kind implements Node.Kind. +func (n *Mark) Kind() gast.NodeKind { + return KindMark +} + +// NewMark returns a new Strikethrough node. +func NewMark() *Mark { + return &Mark{} +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..619698a --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.jlel.se/jlelse/goldmark-mark + +go 1.16 + +require github.com/yuin/goldmark v1.3.7 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..34d25a8 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/yuin/goldmark v1.3.7 h1:NSaHgaeJFCtWXCBkBKXw0rhgMuJ0VoE9FB5mWldcrQ4= +github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= diff --git a/mark.go b/mark.go new file mode 100644 index 0000000..d0fc6eb --- /dev/null +++ b/mark.go @@ -0,0 +1,116 @@ +package mark + +import ( + "git.jlel.se/jlelse/goldmark-mark/ast" + "github.com/yuin/goldmark" + gast "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/renderer" + "github.com/yuin/goldmark/renderer/html" + "github.com/yuin/goldmark/text" + "github.com/yuin/goldmark/util" +) + +type markDelimiterProcessor struct { +} + +func (p *markDelimiterProcessor) IsDelimiter(b byte) bool { + return b == '=' +} + +func (p *markDelimiterProcessor) CanOpenCloser(opener, closer *parser.Delimiter) bool { + return opener.Char == closer.Char +} + +func (p *markDelimiterProcessor) OnMatch(consumes int) gast.Node { + return ast.NewMark() +} + +var defaultMarkDelimiterProcessor = &markDelimiterProcessor{} + +type markParser struct { +} + +var defaultMarkParser = &markParser{} + +// NewMarkParser return a new InlineParser that parses +// mark expressions. +func NewMarkParser() parser.InlineParser { + return defaultMarkParser +} + +func (s *markParser) Trigger() []byte { + return []byte{'='} +} + +func (s *markParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node { + before := block.PrecendingCharacter() + line, segment := block.PeekLine() + node := parser.ScanDelimiter(line, before, 2, defaultMarkDelimiterProcessor) + if node == nil { + return nil + } + node.Segment = segment.WithStop(segment.Start + node.OriginalLength) + block.Advance(node.OriginalLength) + pc.PushDelimiter(node) + return node +} + +func (s *markParser) CloseBlock(parent gast.Node, pc parser.Context) { + // nothing to do +} + +// MarkHTMLRenderer is a renderer.NodeRenderer implementation that +// renders Mark nodes. +type MarkHTMLRenderer struct { + html.Config +} + +// NewMarkHTMLRenderer returns a new MarkHTMLRenderer. +func NewMarkHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { + r := &MarkHTMLRenderer{ + Config: html.NewConfig(), + } + for _, opt := range opts { + opt.SetHTMLOption(&r.Config) + } + return r +} + +// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs. +func (r *MarkHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { + reg.Register(ast.KindMark, r.renderMark) +} + +// MarkAttributeFilter defines attribute names which dd elements can have. +var MarkAttributeFilter = html.GlobalAttributeFilter + +func (r *MarkHTMLRenderer) renderMark(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) { + if entering { + if n.Attributes() != nil { + _, _ = w.WriteString("') + } else { + _, _ = w.WriteString("") + } + } else { + _, _ = w.WriteString("") + } + return gast.WalkContinue, nil +} + +type mark struct { +} + +// Mark is an extension that allow you to use mark expression like '~~text~~' . +var Mark = &mark{} + +func (e *mark) Extend(m goldmark.Markdown) { + m.Parser().AddOptions(parser.WithInlineParsers( + util.Prioritized(NewMarkParser(), 500), + )) + m.Renderer().AddOptions(renderer.WithNodeRenderers( + util.Prioritized(NewMarkHTMLRenderer(), 500), + )) +} diff --git a/mark_test.go b/mark_test.go new file mode 100644 index 0000000..5fb9bee --- /dev/null +++ b/mark_test.go @@ -0,0 +1,21 @@ +package mark + +import ( + "testing" + + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/renderer/html" + "github.com/yuin/goldmark/testutil" +) + +func TestMark(t *testing.T) { + markdown := goldmark.New( + goldmark.WithRendererOptions( + html.WithUnsafe(), + ), + goldmark.WithExtensions( + Mark, + ), + ) + testutil.DoTestCaseFile(markdown, "_test/mark.txt", t, testutil.ParseCliCaseArg()...) +}