-
|
I am implementing my own extension based on the great library Goldmark. The syntax overview is as follows: A new marker Will be parsed as: <p class="content-c1">This should be parsed</p>However, in my implementation code, it is always parsed in the following form: <p class="content-c1">##c1 This should be parsed</p>Below is a brief snippet of my implementation. I used package fbmarkext
import (
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
type content struct{}
// Content is the custom content tag
var Content = &content{}
func (e *content) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(
parser.WithInlineParsers(
util.Prioritized(NewContentParser(), 500),
),
)
}
type ContentParser struct{}
func NewContentParser() parser.InlineParser {
return &ContentParser{}
}
func (p *ContentParser) Trigger() []byte {
return []byte{'#'}
}
func (p *ContentParser) Parse(parent ast.Node, block text.Reader, _ parser.Context) ast.Node {
// Skip if not a paragraph
paragraph, ok := parent.(*ast.Paragraph)
if !ok {
return nil
}
line, segment := block.PeekLine()
// Check 1: Must be at the start of the line (no leading spaces)
if block.LineOffset() > 0 {
return nil
}
// Check 2: Must be the first content in the paragraph
// Verified by checking if the current read position matches the paragraph's starting position
lines := paragraph.Lines()
if lines.Len() == 0 {
return nil
}
firstLine := lines.At(0)
if firstLine.Start != segment.Start {
return nil
}
// Check if it starts with ##c
if len(line) < 4 || line[0] != '#' || line[1] != '#' || line[2] != 'c' {
return nil
}
// Extract the number
numStart := 3
numEnd := numStart
for numEnd < len(line) && line[numEnd] >= '0' && line[numEnd] <= '9' {
numEnd++
}
if numEnd == numStart {
return nil
}
// Check that there must be a space after
if numEnd >= len(line) || line[numEnd] != ' ' {
return nil
}
// Skip the marker part (##c6 + space)
block.Advance(numEnd + 1) // <- This does not take effect
// Set the attribute
className := "content-c" + string(line[numStart:numEnd])
paragraph.SetAttributeString("class", []byte(className))
return nil
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
|
Ah, I see now. I should create a new AST node that consumes the matched marker but does nothing else: // ContentMarkerAST is an empty AST node used to consume the marker characters
type ContentMarkerAST struct {
ast.BaseInline
Segment text.Segment
}
// Dump implements the ast.Dumper interface
func (n *ContentMarkerAST) Dump(source []byte, level int) {
ast.DumpHelper(n, source, level, nil, nil)
}
var KindContentMarker = ast.NewNodeKind("ContentMarkerAST")
// Kind returns the node type
func (n *ContentMarkerAST) Kind() ast.NodeKind {
return KindContentMarker
}
...
marker := &ContentMarkerAST{
Segment: text.NewSegment(segment.Start, segment.Start+numEnd+1),
}
return marker
... |
Beta Was this translation helpful? Give feedback.
Ah, I see now. I should create a new AST node that consumes the matched marker but does nothing else: