Add !embed(...) syntax

This commit is contained in:
Ben Visness 2021-06-16 01:29:16 -05:00
parent 987d379223
commit 643f145071
5 changed files with 155 additions and 6 deletions

Binary file not shown.

View File

@ -1,7 +1,6 @@
package parsing package parsing
import ( import (
"fmt"
"strings" "strings"
"github.com/yuin/goldmark" "github.com/yuin/goldmark"
@ -34,9 +33,7 @@ func (s bParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) g
// link.Protocol = protocol // link.Protocol = protocol
// return link // return link
lineBytes, segment := block.PeekLine() lineBytes, _ := block.PeekLine()
fmt.Printf("line: %s\n", string(lineBytes))
fmt.Printf("segment: %#v\n", segment)
line := string(lineBytes) line := string(lineBytes)

152
src/parsing/embed.go Normal file
View File

@ -0,0 +1,152 @@
package parsing
import (
"regexp"
"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"
)
var (
REEmbedTag = regexp.MustCompile(`^!embed\((?P<url>.+?)\)`)
REYoutubeLong = regexp.MustCompile(`^https://www\.youtube\.com/watch?.*v=(?P<vid>[a-zA-Z0-9_-]{11})`)
REYoutubeShort = regexp.MustCompile(`^https://youtu\.be/(?P<vid>[a-zA-Z0-9_-]{11})`)
REVimeo = regexp.MustCompile(`^https://vimeo\.com/(?P<vid>\d+)`)
)
// ----------------------
// Parser and delimiters
// ----------------------
type embedParser struct{}
// TODO: Make this a block parser
func NewEmbedParser() parser.InlineParser {
return embedParser{}
}
func (s embedParser) Trigger() []byte {
return []byte{'!'}
}
func (s embedParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
restOfLine, _ := block.PeekLine()
urlMatch := REEmbedTag.FindSubmatch(restOfLine)
if urlMatch == nil {
return nil
}
url := urlMatch[REEmbedTag.SubexpIndex("url")]
html := ""
if ytLongMatch := extract(REYoutubeLong, url, "vid"); ytLongMatch != nil {
html = makeYoutubeEmbed(string(ytLongMatch))
} else if ytShortMatch := extract(REYoutubeShort, url, "vid"); ytShortMatch != nil {
html = makeYoutubeEmbed(string(ytShortMatch))
} else if vimeoMatch := extract(REVimeo, url, "vid"); vimeoMatch != nil {
html = `
<div class="mw6">
<div class="aspect-ratio aspect-ratio--16x9">
<iframe class="aspect-ratio--object" src="https://player.vimeo.com/video/` + string(vimeoMatch) + `" frameborder="0" allow="fullscreen; picture-in-picture" allowfullscreen></iframe>
</div>
</div>`
}
if html == "" {
return nil
}
block.Advance(len(urlMatch[0]))
return NewEmbed(html)
}
func extract(re *regexp.Regexp, src []byte, subexpName string) []byte {
m := re.FindSubmatch(src)
if m == nil {
return nil
}
return m[re.SubexpIndex(subexpName)]
}
func makeYoutubeEmbed(vid string) string {
return `
<div class="mw6">
<div class="aspect-ratio aspect-ratio--16x9">
<iframe class="aspect-ratio--object" src="https://www.youtube-nocookie.com/embed/` + vid + `" frameborder="0" allowfullscreen></iframe>
</div>
</div>`
}
// ----------------------
// AST node
// ----------------------
type EmbedNode struct {
gast.BaseBlock
HTML string
}
func (n *EmbedNode) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, nil)
}
var KindEmbed = gast.NewNodeKind("Embed")
func (n *EmbedNode) Kind() gast.NodeKind {
return KindEmbed
}
func NewEmbed(HTML string) gast.Node {
return &EmbedNode{
HTML: HTML,
}
}
// ----------------------
// Renderer
// ----------------------
type EmbedHTMLRenderer struct {
html.Config
}
func NewEmbedHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
r := &EmbedHTMLRenderer{
Config: html.NewConfig(),
}
for _, opt := range opts {
opt.SetHTMLOption(&r.Config)
}
return r
}
func (r *EmbedHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(KindEmbed, r.renderEmbed)
}
func (r *EmbedHTMLRenderer) renderEmbed(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
w.WriteString(n.(*EmbedNode).HTML)
}
return gast.WalkSkipChildren, nil
}
// ----------------------
// Extension
// ----------------------
type EmbedExtension struct{}
func (e EmbedExtension) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewEmbedParser(), 500),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewEmbedHTMLRenderer(), 500),
))
}

View File

@ -91,7 +91,7 @@ func tokenizeBBCode(input string) []token {
} }
var md = goldmark.New( var md = goldmark.New(
goldmark.WithExtensions(extension.GFM, SpoilerExtension{}, bTag{}), goldmark.WithExtensions(extension.GFM, SpoilerExtension{}, EmbedExtension{}, bTag{}),
) )
func ParsePostInput(source string) string { func ParsePostInput(source string) string {

View File

@ -72,7 +72,7 @@ func (n *SpoilerNode) Kind() gast.NodeKind {
return KindSpoiler return KindSpoiler
} }
func NewSpoiler() *SpoilerNode { func NewSpoiler() gast.Node {
return &SpoilerNode{} return &SpoilerNode{}
} }