Browse Source

First implementation

master
Jan-Lukas Else 5 months ago
parent
commit
1a4509a3e8
  1. 2
      LICENSE
  2. 4
      README.md
  3. 18
      _test/mark.txt
  4. 29
      ast/mark.go
  5. 5
      go.mod
  6. 2
      go.sum
  7. 116
      mark.go
  8. 21
      mark_test.go

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:

4
README.md

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

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

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

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

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

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

@ -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…
Cancel
Save