Revert bbcode; restrict scope of ggcode

This commit is contained in:
Ben Visness 2022-09-10 11:09:04 -05:00
parent 9a9ab97c27
commit 18994a2ae6
5 changed files with 158 additions and 160 deletions

Binary file not shown.

View File

@ -7,7 +7,6 @@ import (
"git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/oops" "git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/utils"
"github.com/alecthomas/chroma" "github.com/alecthomas/chroma"
chromahtml "github.com/alecthomas/chroma/formatters/html" chromahtml "github.com/alecthomas/chroma/formatters/html"
"github.com/alecthomas/chroma/lexers" "github.com/alecthomas/chroma/lexers"
@ -29,45 +28,87 @@ var reTag = regexp.MustCompile(`\[\s*(?P<opentagname>[a-zA-Z0-9]+)|\[\s*\/\s*(?P
var previewBBCodeCompiler = bbcode.NewCompiler(false, false) var previewBBCodeCompiler = bbcode.NewCompiler(false, false)
var realBBCodeCompiler = bbcode.NewCompiler(false, false) var realBBCodeCompiler = bbcode.NewCompiler(false, false)
var eduPreviewBBCodeCompiler = bbcode.NewCompiler(false, false)
var eduRealBBCodeCompiler = bbcode.NewCompiler(false, false)
var REYoutubeVidOnly = regexp.MustCompile(`^[a-zA-Z0-9_-]{11}$`) var REYoutubeVidOnly = regexp.MustCompile(`^[a-zA-Z0-9_-]{11}$`)
func init() { func init() {
all := []bbcode.Compiler{previewBBCodeCompiler, realBBCodeCompiler, eduPreviewBBCodeCompiler, eduRealBBCodeCompiler} type attr struct {
real := []bbcode.Compiler{realBBCodeCompiler, eduRealBBCodeCompiler} Name, Value string
preview := []bbcode.Compiler{previewBBCodeCompiler, eduPreviewBBCodeCompiler} }
education := []bbcode.Compiler{eduPreviewBBCodeCompiler, eduRealBBCodeCompiler}
addSimpleTag(all, "h1", "h1", false, nil) addSimpleTag := func(name, tag string, notext bool, attrs ...attr) {
addSimpleTag(all, "h2", "h3", false, nil) var tagFunc bbcode.TagCompilerFunc = func(bn *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
addSimpleTag(all, "h3", "h3", false, nil) if notext {
addSimpleTag(all, "m", "span", false, attrs{"class": "monospace"}) var newChildren []*bbcode.BBCodeNode
addSimpleTag(all, "ol", "ol", true, nil) for _, child := range bn.Children {
addSimpleTag(all, "ul", "ul", true, nil) if child.ID != bbcode.TEXT {
addSimpleTag(all, "li", "li", false, nil) newChildren = append(newChildren, child)
addSimpleTag(all, "spoiler", "span", false, attrs{"class": "spoiler"}) }
addSimpleTag(all, "table", "table", true, nil) }
addSimpleTag(all, "tr", "tr", true, nil) bn.Children = newChildren
addSimpleTag(all, "th", "th", false, nil) }
addSimpleTag(all, "td", "td", false, nil)
addTag(all, "quote", func(bn *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) { out := bbcode.NewHTMLTag("")
out.Name = tag
for _, a := range attrs {
out.Attrs[a.Name] = a.Value
}
return out, true
}
previewBBCodeCompiler.SetTag(name, tagFunc)
realBBCodeCompiler.SetTag(name, tagFunc)
}
addTag := func(name string, f bbcode.TagCompilerFunc) {
previewBBCodeCompiler.SetTag(name, f)
realBBCodeCompiler.SetTag(name, f)
}
previewBBCodeCompiler.SetTag("youtube", makeYoutubeBBCodeFunc(true))
realBBCodeCompiler.SetTag("youtube", makeYoutubeBBCodeFunc(false))
addSimpleTag("h1", "h1", false)
addSimpleTag("h2", "h3", false)
addSimpleTag("h3", "h3", false)
addSimpleTag("m", "span", false, attr{"class", "monospace"})
addSimpleTag("ol", "ol", true)
addSimpleTag("ul", "ul", true)
addSimpleTag("li", "li", false)
addSimpleTag("spoiler", "span", false, attr{"class", "spoiler"})
addSimpleTag("table", "table", true)
addSimpleTag("tr", "tr", true)
addSimpleTag("th", "th", false)
addSimpleTag("td", "td", false)
addTag("quote", func(bn *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
cite := bn.GetOpeningTag().Value cite := bn.GetOpeningTag().Value
if cite == "" { if cite == "" {
return htm("blockquote", nil), true out := bbcode.NewHTMLTag("")
out.Name = "blockquote"
return out, true
} else { } else {
return htm("blockquote", attrs{"cite": cite}, out := bbcode.NewHTMLTag("")
htm("a", attrs{"href": hmnurl.BuildUserProfile(cite), "class": "quotewho"}, out.Name = "blockquote"
bbcode.NewHTMLTag(cite), out.Attrs["cite"] = cite
),
htm("br", nil), a := bbcode.NewHTMLTag("")
), true a.Name = "a"
a.Attrs = map[string]string{
"href": hmnurl.BuildUserProfile(cite),
"class": "quotewho",
}
a.AppendChild(bbcode.NewHTMLTag(cite))
br := bbcode.NewHTMLTag("")
br.Name = "br"
out.AppendChild(a)
out.AppendChild(br)
return out, true
} }
}) })
addTag(all, "code", func(bn *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) { addTag("code", func(bn *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
lang := "" lang := ""
if tagvalue := bn.GetOpeningTag().Value; tagvalue != "" { if tagvalue := bn.GetOpeningTag().Value; tagvalue != "" {
lang = tagvalue lang = tagvalue
@ -109,72 +150,6 @@ func init() {
return out, false return out, false
}) })
addTag(preview, "youtube", makeYoutubeBBCodeFunc(true))
addTag(real, "youtube", makeYoutubeBBCodeFunc(false))
addTag(education, "glossary", func(bn *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
term := bn.GetOpeningTag().Value
return htm("a", attrs{"href": hmnurl.BuildEducationGlossary(term), "class": "glossary-term", "data-term": term}), true
})
addSimpleTag(education, "note", "div", false, attrs{"class": "education-note"})
addTag(education, "resource", func(bn *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
name, _ := bn.GetOpeningTag().Args["name"]
url, _ := bn.GetOpeningTag().Args["url"]
res := htm("a", attrs{"class": "education-resource", "href": url, "target": "_blank"},
htm("h2", nil, bbcode.NewHTMLTag(utils.OrDefault(name, `hey you, do name="Some Resource" url="whatever"`))),
)
return res, true
})
}
type attrs map[string]string
// Generates a "simple" tag, i.e. one that is more or less just HTML.
//
// name is the bbcode tag name.
// tag is the HTML tag to generate.
// notext prevents the tag from having any direct text children (useful for e.g. [ol] and [ul])
// attrs are any HTML attributes to add to the tag.
func addSimpleTag(compilers []bbcode.Compiler, name, tag string, notext bool, attributes attrs) {
var tagFunc bbcode.TagCompilerFunc = func(bn *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
if notext {
var newChildren []*bbcode.BBCodeNode
for _, child := range bn.Children {
if child.ID != bbcode.TEXT {
newChildren = append(newChildren, child)
}
}
bn.Children = newChildren
}
out := bbcode.NewHTMLTag("")
out.Name = tag
out.Attrs = attributes
return out, true
}
for _, compiler := range compilers {
compiler.SetTag(name, tagFunc)
}
}
func addTag(compilers []bbcode.Compiler, name string, f bbcode.TagCompilerFunc) {
for _, compiler := range compilers {
compiler.SetTag(name, f)
}
}
// html was taken
func htm(tag string, attributes attrs, children ...*bbcode.HTMLTag) *bbcode.HTMLTag {
res := bbcode.NewHTMLTag("")
res.Name = tag
res.Attrs = attributes
for _, child := range children {
res.AppendChild(child)
}
return res
} }
func makeYoutubeBBCodeFunc(preview bool) bbcode.TagCompilerFunc { func makeYoutubeBBCodeFunc(preview bool) bbcode.TagCompilerFunc {
@ -258,8 +233,7 @@ func makeYoutubeBBCodeFunc(preview bool) bbcode.TagCompilerFunc {
// ---------------------- // ----------------------
type bbcodeParser struct { type bbcodeParser struct {
Preview bool Preview bool
Education bool
} }
var _ parser.InlineParser = &bbcodeParser{} var _ parser.InlineParser = &bbcodeParser{}
@ -317,15 +291,9 @@ func (s bbcodeParser) Parse(parent gast.Node, block text.Reader, pc parser.Conte
unparsedBBCode := restOfSource[:endIndex] unparsedBBCode := restOfSource[:endIndex]
block.Advance(len(unparsedBBCode)) block.Advance(len(unparsedBBCode))
var compiler bbcode.Compiler compiler := realBBCodeCompiler
if s.Preview && s.Education { if s.Preview {
compiler = eduPreviewBBCodeCompiler
} else if s.Preview && !s.Education {
compiler = previewBBCodeCompiler compiler = previewBBCodeCompiler
} else if !s.Preview && s.Education {
compiler = eduRealBBCodeCompiler
} else {
compiler = realBBCodeCompiler
} }
return NewBBCode(compiler.Compile(string(unparsedBBCode))) return NewBBCode(compiler.Compile(string(unparsedBBCode)))
@ -400,13 +368,12 @@ func (r *BBCodeHTMLRenderer) renderBBCode(w util.BufWriter, source []byte, n gas
// ---------------------- // ----------------------
type BBCodeExtension struct { type BBCodeExtension struct {
Preview bool Preview bool
Education bool
} }
func (e BBCodeExtension) Extend(m goldmark.Markdown) { func (e BBCodeExtension) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers( m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(bbcodeParser{Preview: e.Preview, Education: e.Education}, BBCodePriority), util.Prioritized(bbcodeParser{Preview: e.Preview}, BBCodePriority),
)) ))
m.Renderer().AddOptions(renderer.WithNodeRenderers( m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewBBCodeHTMLRenderer(), BBCodePriority), util.Prioritized(NewBBCodeHTMLRenderer(), BBCodePriority),

View File

@ -38,39 +38,68 @@ import (
// !!! // !!!
// //
type ggcodeRenderer = func(w util.BufWriter, n *ggcodeNode, entering bool) error var ggcodeTags = map[string]ggcodeTag{
"glossary": {
Filter: ggcodeFilterEdu,
Renderer: func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error {
if entering {
term, _ := n.Args["term"]
c.W.WriteString(fmt.Sprintf(
`<a href="%s" class="glossary-term" data-term="%s">`,
hmnurl.BuildEducationGlossary(term),
term,
))
} else {
c.W.WriteString("</a>")
}
return nil
},
},
"resource": {
Filter: ggcodeFilterEdu,
Renderer: func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error {
if entering {
c.W.WriteString(`<div class="edu-resource">`)
c.W.WriteString(fmt.Sprintf(` <a href="%s" target="_blank"><h2>%s</h2></a>`, n.Args["url"], utils.OrDefault(n.Args["name"], "[missing `name`]")))
} else {
c.W.WriteString("</div>")
}
return nil
},
},
"note": {
Filter: ggcodeFilterEdu,
Renderer: func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error {
if entering {
c.W.WriteString(`<span class="note">`)
} else {
c.W.WriteString(`</span>`)
}
return nil
},
},
}
var ggcodeTags = map[string]ggcodeRenderer{ // ----------------------
"glossary": func(w util.BufWriter, n *ggcodeNode, entering bool) error { // Types
if entering { // ----------------------
term, _ := n.Args["term"]
w.WriteString(fmt.Sprintf( type ggcodeRendererContext struct {
`<a href="%s" class="glossary-term" data-term="%s">`, W util.BufWriter
hmnurl.BuildEducationGlossary(term), Source []byte
term, Opts MarkdownOptions
)) }
} else {
w.WriteString("</a>") type ggcodeTagFilter func(opts MarkdownOptions) bool
} type ggcodeRenderer func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error
return nil
}, type ggcodeTag struct {
"resource": func(w util.BufWriter, n *ggcodeNode, entering bool) error { Filter ggcodeTagFilter
if entering { Renderer ggcodeRenderer
w.WriteString(`<div class="edu-resource">`) }
w.WriteString(fmt.Sprintf(` <a href="%s" target="_blank"><h2>%s</h2></a>`, n.Args["url"], utils.OrDefault(n.Args["name"], "[missing `name`]")))
} else { var ggcodeFilterEdu ggcodeTagFilter = func(opts MarkdownOptions) bool {
w.WriteString("</div>") return opts.Education
}
return nil
},
"note": func(w util.BufWriter, n *ggcodeNode, entering bool) error {
if entering {
w.WriteString(`<span class="note">`)
} else {
w.WriteString(`</span>`)
}
return nil
},
} }
// ---------------------- // ----------------------
@ -83,9 +112,7 @@ var reGGCodeArgs = regexp.MustCompile(`(?P<arg>[a-zA-Z0-9-_]+)="(?P<val>.*?)"`)
// Block parser stuff // Block parser stuff
type ggcodeBlockParser struct { type ggcodeBlockParser struct{}
Preview bool
}
var _ parser.BlockParser = ggcodeBlockParser{} var _ parser.BlockParser = ggcodeBlockParser{}
@ -139,9 +166,7 @@ func (s ggcodeBlockParser) CanAcceptIndentedLine() bool {
// Inline parser stuff // Inline parser stuff
type ggcodeInlineParser struct { type ggcodeInlineParser struct{}
Preview bool
}
var _ parser.InlineParser = ggcodeInlineParser{} var _ parser.InlineParser = ggcodeInlineParser{}
@ -229,10 +254,12 @@ func (n *ggcodeNode) Kind() gast.NodeKind {
type ggcodeHTMLRenderer struct { type ggcodeHTMLRenderer struct {
html.Config html.Config
Opts MarkdownOptions
} }
func newGGCodeHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { func newGGCodeHTMLRenderer(markdownOpts MarkdownOptions, opts ...html.Option) renderer.NodeRenderer {
r := &ggcodeHTMLRenderer{ r := &ggcodeHTMLRenderer{
Opts: markdownOpts,
Config: html.NewConfig(), Config: html.NewConfig(),
} }
for _, opt := range opts { for _, opt := range opts {
@ -248,16 +275,22 @@ func (r *ggcodeHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegister
func (r *ggcodeHTMLRenderer) render(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) { func (r *ggcodeHTMLRenderer) render(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
node := n.(*ggcodeNode) node := n.(*ggcodeNode)
var renderer ggcodeRenderer = defaultGGCodeRenderer var renderer ggcodeRenderer = defaultGGCodeRenderer
if tagRenderer, ok := ggcodeTags[node.Name]; ok { if tag, ok := ggcodeTags[node.Name]; ok {
renderer = tagRenderer if tag.Filter == nil || tag.Filter(r.Opts) {
renderer = tag.Renderer
}
} }
err := renderer(w, node, entering) err := renderer(ggcodeRendererContext{
W: w,
Source: source,
Opts: r.Opts,
}, node, entering)
return gast.WalkContinue, err return gast.WalkContinue, err
} }
func defaultGGCodeRenderer(w util.BufWriter, n *ggcodeNode, entering bool) error { func defaultGGCodeRenderer(c ggcodeRendererContext, n *ggcodeNode, entering bool) error {
if entering { if entering {
w.WriteString("wat is this tag >:(") c.W.WriteString("[unknown ggcode tag]")
} }
return nil return nil
} }
@ -267,17 +300,17 @@ func defaultGGCodeRenderer(w util.BufWriter, n *ggcodeNode, entering bool) error
// ---------------------- // ----------------------
type ggcodeExtension struct { type ggcodeExtension struct {
Preview bool Opts MarkdownOptions
} }
func (e ggcodeExtension) Extend(m goldmark.Markdown) { func (e ggcodeExtension) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithBlockParsers( m.Parser().AddOptions(parser.WithBlockParsers(
util.Prioritized(ggcodeBlockParser{Preview: e.Preview}, 500), util.Prioritized(ggcodeBlockParser{}, 500),
)) ))
m.Parser().AddOptions(parser.WithInlineParsers( m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(ggcodeInlineParser{Preview: e.Preview}, 500), util.Prioritized(ggcodeInlineParser{}, 500),
)) ))
m.Renderer().AddOptions(renderer.WithNodeRenderers( m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(newGGCodeHTMLRenderer(), 500), util.Prioritized(newGGCodeHTMLRenderer(e.Opts), 500),
)) ))
} }

View File

@ -133,11 +133,10 @@ func makeGoldmarkExtensions(opts MarkdownOptions) []goldmark.Extender {
extenders = append(extenders, extenders = append(extenders,
MathjaxExtension{}, MathjaxExtension{},
BBCodeExtension{ BBCodeExtension{
Preview: opts.Previews, Preview: opts.Previews,
Education: opts.Education,
}, },
ggcodeExtension{ ggcodeExtension{
Preview: opts.Previews, Opts: opts,
}, },
) )

View File

@ -347,9 +347,8 @@ func getEduArticleFromForm(form url.Values) (art models.EduArticle, ver models.E
} }
art.Published = form.Get("published") != "" art.Published = form.Get("published") != ""
// TODO: Education-specific Markdown
ver.ContentRaw = form.Get("body") ver.ContentRaw = form.Get("body")
ver.ContentHTML = parsing.ParseMarkdown(ver.ContentRaw, parsing.ForumRealMarkdown) ver.ContentHTML = parsing.ParseMarkdown(ver.ContentRaw, parsing.EducationRealMarkdown)
return return
} }