1
Fork 0

First implementation

This commit is contained in:
Jan-Lukas Else 2021-05-22 18:14:10 +02:00
parent 1469fef2b3
commit 1a4509a3e8
8 changed files with 195 additions and 2 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) <year> <copyright holders>
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:

View File

@ -1,3 +1,5 @@
# goldmark-mark
Goldmark extension for HTML mark tags
Goldmark extension for HTML mark tags
This is heavily based on the strikethrough extension of Goldmark.

18
_test/mark.txt Normal file
View File

@ -0,0 +1,18 @@
1
//- - - - - - - - -//
==Hi== Hello, world!
//- - - - - - - - -//
<p><mark>Hi</mark> Hello, world!</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//
2
//- - - - - - - - -//
This ==has a
new paragraph==.
//- - - - - - - - -//
<p>This ==has a</p>
<p>new paragraph==.</p>
//= = = = = = = = = = = = = = = = = = = = = = = =//

29
ast/mark.go Normal file
View File

@ -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{}
}

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module git.jlel.se/jlelse/goldmark-mark
go 1.16
require github.com/yuin/goldmark v1.3.7

2
go.sum Normal file
View File

@ -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=

116
mark.go Normal file
View File

@ -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("<mark")
html.RenderAttributes(w, n, MarkAttributeFilter)
_ = w.WriteByte('>')
} else {
_, _ = w.WriteString("<mark>")
}
} else {
_, _ = w.WriteString("</mark>")
}
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),
))
}

21
mark_test.go Normal file
View File

@ -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()...)
}