First implementation
This commit is contained in:
parent
1469fef2b3
commit
1a4509a3e8
2
LICENSE
2
LICENSE
|
@ -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:
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -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>
|
||||
//= = = = = = = = = = = = = = = = = = = = = = = =//
|
|
@ -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{}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module git.jlel.se/jlelse/goldmark-mark
|
||||
|
||||
go 1.16
|
||||
|
||||
require github.com/yuin/goldmark v1.3.7
|
|
@ -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=
|
|
@ -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),
|
||||
))
|
||||
}
|
|
@ -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()...)
|
||||
}
|
Loading…
Reference in New Issue