From b496df9712e42f106da94d57ae309fb68d83c36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sat, 4 May 2024 20:25:08 +0200 Subject: [PATCH] extras: Some refactorings * Move ast package one level up * Unexport most types * Simplify config Closes #19 --- .github/workflows/test.yml | 2 +- extras/ast.go | 84 +++++++++++++++++++++++++++ extras/ast/inline.go | 106 ---------------------------------- extras/inline.go | 113 +++++++++++++++++++++++-------------- extras/inline_test.go | 28 ++++----- 5 files changed, 171 insertions(+), 162 deletions(-) create mode 100644 extras/ast.go delete mode 100644 extras/ast/inline.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1517e31..d70b4c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ jobs: matrix: go-version: [1.21.x, 1.22.x] platform: [ubuntu-latest, macos-latest, windows-latest] - package: [passthrough] + package: [passthrough, extras] runs-on: ${{ matrix.platform }} defaults: run: diff --git a/extras/ast.go b/extras/ast.go new file mode 100644 index 0000000..c790bde --- /dev/null +++ b/extras/ast.go @@ -0,0 +1,84 @@ +package extras + +import ( + "github.com/yuin/goldmark/ast" +) + +type inlineTag struct { + TagKind ast.NodeKind + Char byte + Number int + Html string + WhitespaceAllowed bool + ParsePriority int + RenderPriority int +} + +var superscriptTag = inlineTag{ + TagKind: kindSuperscript, + Char: '^', + Number: 1, + Html: "sup", + WhitespaceAllowed: false, + ParsePriority: 600, + RenderPriority: 600, +} + +var subscriptTag = inlineTag{ + TagKind: kindSubscript, + Char: '~', + Number: 1, + Html: "sub", + WhitespaceAllowed: false, + ParsePriority: 602, + RenderPriority: 602, +} + +var insertTag = inlineTag{ + TagKind: kindInsert, + Char: '+', + Number: 2, + Html: "ins", + WhitespaceAllowed: true, + ParsePriority: 501, + RenderPriority: 501, +} + +var markTag = inlineTag{ + TagKind: kindMark, + Char: '=', + Number: 2, + Html: "mark", + WhitespaceAllowed: true, + ParsePriority: 550, + RenderPriority: 550, +} + +type inlineTagNode struct { + ast.BaseInline + + inlineTag +} + +func newInlineTag(tag inlineTag) *inlineTagNode { + return &inlineTagNode{ + BaseInline: ast.BaseInline{}, + + inlineTag: tag, + } +} + +var ( + kindSuperscript = ast.NewNodeKind("Superscript") + kindSubscript = ast.NewNodeKind("Subscript") + kindInsert = ast.NewNodeKind("Insert") + kindMark = ast.NewNodeKind("Mark") +) + +func (n *inlineTagNode) Kind() ast.NodeKind { + return n.TagKind +} + +func (n *inlineTagNode) Dump(source []byte, level int) { + ast.DumpHelper(n, source, level, nil, nil) +} diff --git a/extras/ast/inline.go b/extras/ast/inline.go deleted file mode 100644 index 512105b..0000000 --- a/extras/ast/inline.go +++ /dev/null @@ -1,106 +0,0 @@ -package ast - -import ( - gast "github.com/yuin/goldmark/ast" -) - -type InlineTagType int - -const ( - Superscript InlineTagType = iota + 1 - Subscript - Insert - Mark -) - -type InlineTag struct { - TagType InlineTagType - Char byte - Number int - Html string - WhitespaceAllowed bool - ParsePriority int - RenderPriority int -} - -var SuperscriptTag = InlineTag{ - TagType: Superscript, - Char: '^', - Number: 1, - Html: "sup", - WhitespaceAllowed: false, - ParsePriority: 600, - RenderPriority: 600, -} - -var SubscriptTag = InlineTag{ - TagType: Subscript, - Char: '~', - Number: 1, - Html: "sub", - WhitespaceAllowed: false, - ParsePriority: 602, - RenderPriority: 602, -} - -var InsertTag = InlineTag{ - TagType: Insert, - Char: '+', - Number: 2, - Html: "ins", - WhitespaceAllowed: true, - ParsePriority: 501, - RenderPriority: 501, -} - -var MarkTag = InlineTag{ - TagType: Mark, - Char: '=', - Number: 2, - Html: "mark", - WhitespaceAllowed: true, - ParsePriority: 550, - RenderPriority: 550, -} - -type InlineTagNode struct { - gast.BaseInline - - InlineTag -} - -func NewInlineTag(tag InlineTag) *InlineTagNode { - return &InlineTagNode{ - BaseInline: gast.BaseInline{}, - - InlineTag: tag, - } -} - -var KindSuperscript = gast.NewNodeKind("Superscript") -var KindSubscript = gast.NewNodeKind("Subscript") -var KindInsert = gast.NewNodeKind("Insert") -var KindMark = gast.NewNodeKind("Mark") - -func NewInlineTagNodeKind(tag InlineTagType) gast.NodeKind { - var kind gast.NodeKind - switch tag { - case Superscript: - kind = KindSuperscript - case Subscript: - kind = KindSubscript - case Insert: - kind = KindInsert - case Mark: - kind = KindMark - } - return kind -} - -func (n *InlineTagNode) Kind() gast.NodeKind { - return NewInlineTagNodeKind(n.TagType) -} - -func (n *InlineTagNode) Dump(source []byte, level int) { - gast.DumpHelper(n, source, level, nil, nil) -} diff --git a/extras/inline.go b/extras/inline.go index 87c71f6..094121a 100644 --- a/extras/inline.go +++ b/extras/inline.go @@ -1,9 +1,8 @@ package extras import ( - "github.com/gohugoio/hugo-goldmark-extensions/extras/ast" "github.com/yuin/goldmark" - gast "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/renderer" "github.com/yuin/goldmark/renderer/html" @@ -12,10 +11,10 @@ import ( ) type inlineTagDelimiterProcessor struct { - ast.InlineTag + inlineTag } -func newInlineTagDelimiterProcessor(tag ast.InlineTag) parser.DelimiterProcessor { +func newInlineTagDelimiterProcessor(tag inlineTag) parser.DelimiterProcessor { return &inlineTagDelimiterProcessor{tag} } @@ -27,16 +26,16 @@ func (p *inlineTagDelimiterProcessor) CanOpenCloser(opener, closer *parser.Delim return opener.Char == closer.Char } -func (p *inlineTagDelimiterProcessor) OnMatch(_ int) gast.Node { - return ast.NewInlineTag(p.InlineTag) +func (p *inlineTagDelimiterProcessor) OnMatch(_ int) ast.Node { + return newInlineTag(p.inlineTag) } type inlineTagParser struct { - ast.InlineTag + inlineTag } -func newInlineTagParser(tag ast.InlineTag) parser.InlineParser { - return &inlineTagParser{InlineTag: tag} +func newInlineTagParser(tag inlineTag) parser.InlineParser { + return &inlineTagParser{inlineTag: tag} } // Trigger implements parser.InlineParser. @@ -45,10 +44,10 @@ func (s *inlineTagParser) Trigger() []byte { } // Parse implements the parser.InlineParser for all types of InlineTags. -func (s *inlineTagParser) Parse(_ gast.Node, block text.Reader, pc parser.Context) gast.Node { +func (s *inlineTagParser) Parse(_ ast.Node, block text.Reader, pc parser.Context) ast.Node { before := block.PrecendingCharacter() line, segment := block.PeekLine() - node := parser.ScanDelimiter(line, before, s.Number, newInlineTagDelimiterProcessor(s.InlineTag)) + node := parser.ScanDelimiter(line, before, s.Number, newInlineTagDelimiterProcessor(s.inlineTag)) if node == nil { return nil } @@ -80,15 +79,15 @@ func hasSpace(line []byte) bool { type inlineTagHTMLRenderer struct { htmlTag string - tagType ast.InlineTagType + tagKind ast.NodeKind html.Config } -// newInlineTagHTMLRenderer returns a new NodeRenderer that renders InlineTagNode nodes to HTML. -func newInlineTagHTMLRenderer(tag ast.InlineTag, opts ...html.Option) renderer.NodeRenderer { +// newInlineTagHTMLRenderer returns a new NodeRenderer that renders InlineTaast.Node nodes to HTML. +func newInlineTagHTMLRenderer(tag inlineTag, opts ...html.Option) renderer.NodeRenderer { r := &inlineTagHTMLRenderer{ htmlTag: tag.Html, - tagType: tag.TagType, + tagKind: tag.TagKind, Config: html.NewConfig(), } for _, opt := range opts { @@ -99,7 +98,7 @@ func newInlineTagHTMLRenderer(tag ast.InlineTag, opts ...html.Option) renderer.N // RegisterFuncs registers rendering functions to the given NodeRendererFuncRegisterer. func (r *inlineTagHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { - reg.Register(ast.NewInlineTagNodeKind(r.tagType), r.renderInlineTag) + reg.Register(r.tagKind, r.renderInlineTag) } // inlineTagAttributeFilter is a global filter for attributes. @@ -107,7 +106,8 @@ var inlineTagAttributeFilter = html.GlobalAttributeFilter // renderInlineTag renders an inline tag. func (r *inlineTagHTMLRenderer) renderInlineTag( - w util.BufWriter, _ []byte, n gast.Node, entering bool) (gast.WalkStatus, error) { + w util.BufWriter, _ []byte, n ast.Node, entering bool, +) (ast.WalkStatus, error) { if entering { _ = w.WriteByte('<') _, _ = w.WriteString(r.htmlTag) @@ -119,41 +119,70 @@ func (r *inlineTagHTMLRenderer) renderInlineTag( _, _ = w.WriteString(r.htmlTag) } _ = w.WriteByte('>') - return gast.WalkContinue, nil + return ast.WalkContinue, nil } -// inlineTag is an extension that adds inline tags to the Markdown parser and renderer. -type inlineTag struct { - ast.InlineTag +// inlineExtension is an extension that adds inline tags to the Markdown parser and renderer. +type inlineExtension struct { + conf Config } -// InlineTagConfig is a configuration struct for the ExtraInlineTag extension. +// Config confitures the extras extension. type Config struct { - ast.InlineTagType + Superscript SuperscriptConfig + Subscript SubscriptConfig + Insert InsertConfig + Mark MarkConfig } +// SuperscriptConfig configures the superscript extension. +type SuperscriptConfig struct { + Enable bool +} + +// SubscriptConfig configures the subscript extension. +type SubscriptConfig struct { + Enable bool +} + +// InsertConfig configures the insert extension. +type InsertConfig struct { + Enable bool +} + +// MarkConfig configures the mark extension. +type MarkConfig struct { + Enable bool +} + +// New returns a new inline tag extension. + func New(config Config) goldmark.Extender { - var extension inlineTag - - switch config.InlineTagType { - case ast.Superscript: - extension = inlineTag{ast.SuperscriptTag} - case ast.Subscript: - extension = inlineTag{ast.SubscriptTag} - case ast.Insert: - extension = inlineTag{ast.InsertTag} - case ast.Mark: - extension = inlineTag{ast.MarkTag} + return &inlineExtension{ + conf: config, } - return &extension } // Extend adds inline tags to the Markdown parser and renderer. -func (tag *inlineTag) Extend(md goldmark.Markdown) { - md.Parser().AddOptions(parser.WithInlineParsers( - util.Prioritized(newInlineTagParser(tag.InlineTag), tag.ParsePriority), - )) - md.Renderer().AddOptions(renderer.WithNodeRenderers( - util.Prioritized(newInlineTagHTMLRenderer(tag.InlineTag), tag.RenderPriority), - )) +func (tag *inlineExtension) Extend(md goldmark.Markdown) { + addTag := func(tag inlineTag) { + md.Parser().AddOptions(parser.WithInlineParsers( + util.Prioritized(newInlineTagParser(tag), tag.ParsePriority), + )) + md.Renderer().AddOptions(renderer.WithNodeRenderers( + util.Prioritized(newInlineTagHTMLRenderer(tag), tag.RenderPriority), + )) + } + if tag.conf.Superscript.Enable { + addTag(superscriptTag) + } + if tag.conf.Subscript.Enable { + addTag(subscriptTag) + } + if tag.conf.Insert.Enable { + addTag(insertTag) + } + if tag.conf.Mark.Enable { + addTag(markTag) + } } diff --git a/extras/inline_test.go b/extras/inline_test.go index 69bba40..9823d3d 100644 --- a/extras/inline_test.go +++ b/extras/inline_test.go @@ -1,25 +1,28 @@ -package extras +package extras_test import ( "bytes" - xast "github.com/gohugoio/hugo-goldmark-extensions/extras/ast" + "testing" + + "github.com/gohugoio/hugo-goldmark-extensions/extras" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/text" - "testing" "github.com/yuin/goldmark" "github.com/yuin/goldmark/testutil" ) -func buildGoldmarkWithInlineTag(tag xast.InlineTagType) goldmark.Markdown { - return goldmark.New(goldmark.WithExtensions(New(Config{InlineTagType: tag}))) +func buildGoldmarkWithInlineTag(conf extras.Config) goldmark.Markdown { + return goldmark.New(goldmark.WithExtensions(extras.New(conf))) } -var markdown = goldmark.New() -var markdownWithSuperscript = buildGoldmarkWithInlineTag(xast.Superscript) -var markdownWithSubscript = buildGoldmarkWithInlineTag(xast.Subscript) -var markdownWithInsert = buildGoldmarkWithInlineTag(xast.Insert) -var markdownWithMark = buildGoldmarkWithInlineTag(xast.Mark) +var ( + markdown = goldmark.New() + markdownWithSuperscript = buildGoldmarkWithInlineTag(extras.Config{Superscript: extras.SuperscriptConfig{Enable: true}}) + markdownWithSubscript = buildGoldmarkWithInlineTag(extras.Config{Subscript: extras.SubscriptConfig{Enable: true}}) + markdownWithInsert = buildGoldmarkWithInlineTag(extras.Config{Insert: extras.InsertConfig{Enable: true}}) + markdownWithMark = buildGoldmarkWithInlineTag(extras.Config{Mark: extras.MarkConfig{Enable: true}}) +) func TestSuperscript(t *testing.T) { testutil.DoTestCaseFile(markdownWithSuperscript, "_test/superscript.txt", t, testutil.ParseCliCaseArg()...) @@ -59,9 +62,8 @@ This formula contains one superscript: f(x) = x^2^ .` } func TestSubscript(t *testing.T) { - var markdown = goldmark.New( - goldmark.WithExtensions(New(Config{ - InlineTagType: xast.Subscript}), extension.Strikethrough)) + markdown := goldmark.New( + goldmark.WithExtensions(extras.New(extras.Config{Subscript: extras.SubscriptConfig{Enable: true}}), extension.Strikethrough)) testutil.DoTestCaseFile(markdown, "_test/subscript.txt", t, testutil.ParseCliCaseArg()...) }