diff --git a/public/parsing.wasm b/public/parsing.wasm index 65a23b5..646301a 100755 Binary files a/public/parsing.wasm and b/public/parsing.wasm differ diff --git a/src/parsing/bbcode.go b/src/parsing/bbcode.go index f63b1cb..ed98685 100644 --- a/src/parsing/bbcode.go +++ b/src/parsing/bbcode.go @@ -7,7 +7,6 @@ import ( "git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/oops" - "git.handmade.network/hmn/hmn/src/utils" "github.com/alecthomas/chroma" chromahtml "github.com/alecthomas/chroma/formatters/html" "github.com/alecthomas/chroma/lexers" @@ -29,45 +28,87 @@ var reTag = regexp.MustCompile(`\[\s*(?P[a-zA-Z0-9]+)|\[\s*\/\s*(?P var previewBBCodeCompiler = 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}$`) func init() { - all := []bbcode.Compiler{previewBBCodeCompiler, realBBCodeCompiler, eduPreviewBBCodeCompiler, eduRealBBCodeCompiler} - real := []bbcode.Compiler{realBBCodeCompiler, eduRealBBCodeCompiler} - preview := []bbcode.Compiler{previewBBCodeCompiler, eduPreviewBBCodeCompiler} - education := []bbcode.Compiler{eduPreviewBBCodeCompiler, eduRealBBCodeCompiler} + type attr struct { + Name, Value string + } - addSimpleTag(all, "h1", "h1", false, nil) - addSimpleTag(all, "h2", "h3", false, nil) - addSimpleTag(all, "h3", "h3", false, nil) - addSimpleTag(all, "m", "span", false, attrs{"class": "monospace"}) - addSimpleTag(all, "ol", "ol", true, nil) - addSimpleTag(all, "ul", "ul", true, nil) - addSimpleTag(all, "li", "li", false, nil) - addSimpleTag(all, "spoiler", "span", false, attrs{"class": "spoiler"}) - addSimpleTag(all, "table", "table", true, nil) - addSimpleTag(all, "tr", "tr", true, nil) - addSimpleTag(all, "th", "th", false, nil) - addSimpleTag(all, "td", "td", false, nil) + addSimpleTag := func(name, tag string, notext bool, attrs ...attr) { + 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 + } - 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 if cite == "" { - return htm("blockquote", nil), true + out := bbcode.NewHTMLTag("") + out.Name = "blockquote" + return out, true } else { - return htm("blockquote", attrs{"cite": cite}, - htm("a", attrs{"href": hmnurl.BuildUserProfile(cite), "class": "quotewho"}, - bbcode.NewHTMLTag(cite), - ), - htm("br", nil), - ), true + out := bbcode.NewHTMLTag("") + out.Name = "blockquote" + out.Attrs["cite"] = cite + + a := bbcode.NewHTMLTag("") + 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 := "" if tagvalue := bn.GetOpeningTag().Value; tagvalue != "" { lang = tagvalue @@ -109,72 +150,6 @@ func init() { 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 { @@ -258,8 +233,7 @@ func makeYoutubeBBCodeFunc(preview bool) bbcode.TagCompilerFunc { // ---------------------- type bbcodeParser struct { - Preview bool - Education bool + Preview bool } var _ parser.InlineParser = &bbcodeParser{} @@ -317,15 +291,9 @@ func (s bbcodeParser) Parse(parent gast.Node, block text.Reader, pc parser.Conte unparsedBBCode := restOfSource[:endIndex] block.Advance(len(unparsedBBCode)) - var compiler bbcode.Compiler - if s.Preview && s.Education { - compiler = eduPreviewBBCodeCompiler - } else if s.Preview && !s.Education { + compiler := realBBCodeCompiler + if s.Preview { compiler = previewBBCodeCompiler - } else if !s.Preview && s.Education { - compiler = eduRealBBCodeCompiler - } else { - compiler = realBBCodeCompiler } return NewBBCode(compiler.Compile(string(unparsedBBCode))) @@ -400,13 +368,12 @@ func (r *BBCodeHTMLRenderer) renderBBCode(w util.BufWriter, source []byte, n gas // ---------------------- type BBCodeExtension struct { - Preview bool - Education bool + Preview bool } func (e BBCodeExtension) Extend(m goldmark.Markdown) { 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( util.Prioritized(NewBBCodeHTMLRenderer(), BBCodePriority), diff --git a/src/parsing/ggcode.go b/src/parsing/ggcode.go index d6bae80..e974909 100644 --- a/src/parsing/ggcode.go +++ b/src/parsing/ggcode.go @@ -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( + ``, + hmnurl.BuildEducationGlossary(term), + term, + )) + } else { + c.W.WriteString("") + } + return nil + }, + }, + "resource": { + Filter: ggcodeFilterEdu, + Renderer: func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error { + if entering { + c.W.WriteString(`
`) + c.W.WriteString(fmt.Sprintf(`

%s

`, n.Args["url"], utils.OrDefault(n.Args["name"], "[missing `name`]"))) + } else { + c.W.WriteString("
") + } + return nil + }, + }, + "note": { + Filter: ggcodeFilterEdu, + Renderer: func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error { + if entering { + c.W.WriteString(``) + } else { + c.W.WriteString(``) + } + return nil + }, + }, +} -var ggcodeTags = map[string]ggcodeRenderer{ - "glossary": func(w util.BufWriter, n *ggcodeNode, entering bool) error { - if entering { - term, _ := n.Args["term"] - w.WriteString(fmt.Sprintf( - ``, - hmnurl.BuildEducationGlossary(term), - term, - )) - } else { - w.WriteString("") - } - return nil - }, - "resource": func(w util.BufWriter, n *ggcodeNode, entering bool) error { - if entering { - w.WriteString(`
`) - w.WriteString(fmt.Sprintf(`

%s

`, n.Args["url"], utils.OrDefault(n.Args["name"], "[missing `name`]"))) - } else { - w.WriteString("
") - } - return nil - }, - "note": func(w util.BufWriter, n *ggcodeNode, entering bool) error { - if entering { - w.WriteString(``) - } else { - w.WriteString(``) - } - return nil - }, +// ---------------------- +// Types +// ---------------------- + +type ggcodeRendererContext struct { + W util.BufWriter + Source []byte + Opts MarkdownOptions +} + +type ggcodeTagFilter func(opts MarkdownOptions) bool +type ggcodeRenderer func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error + +type ggcodeTag struct { + Filter ggcodeTagFilter + Renderer ggcodeRenderer +} + +var ggcodeFilterEdu ggcodeTagFilter = func(opts MarkdownOptions) bool { + return opts.Education } // ---------------------- @@ -83,9 +112,7 @@ var reGGCodeArgs = regexp.MustCompile(`(?P[a-zA-Z0-9-_]+)="(?P.*?)"`) // Block parser stuff -type ggcodeBlockParser struct { - Preview bool -} +type ggcodeBlockParser struct{} var _ parser.BlockParser = ggcodeBlockParser{} @@ -139,9 +166,7 @@ func (s ggcodeBlockParser) CanAcceptIndentedLine() bool { // Inline parser stuff -type ggcodeInlineParser struct { - Preview bool -} +type ggcodeInlineParser struct{} var _ parser.InlineParser = ggcodeInlineParser{} @@ -229,10 +254,12 @@ func (n *ggcodeNode) Kind() gast.NodeKind { type ggcodeHTMLRenderer struct { html.Config + Opts MarkdownOptions } -func newGGCodeHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { +func newGGCodeHTMLRenderer(markdownOpts MarkdownOptions, opts ...html.Option) renderer.NodeRenderer { r := &ggcodeHTMLRenderer{ + Opts: markdownOpts, Config: html.NewConfig(), } 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) { node := n.(*ggcodeNode) var renderer ggcodeRenderer = defaultGGCodeRenderer - if tagRenderer, ok := ggcodeTags[node.Name]; ok { - renderer = tagRenderer + if tag, ok := ggcodeTags[node.Name]; ok { + 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 } -func defaultGGCodeRenderer(w util.BufWriter, n *ggcodeNode, entering bool) error { +func defaultGGCodeRenderer(c ggcodeRendererContext, n *ggcodeNode, entering bool) error { if entering { - w.WriteString("wat is this tag >:(") + c.W.WriteString("[unknown ggcode tag]") } return nil } @@ -267,17 +300,17 @@ func defaultGGCodeRenderer(w util.BufWriter, n *ggcodeNode, entering bool) error // ---------------------- type ggcodeExtension struct { - Preview bool + Opts MarkdownOptions } func (e ggcodeExtension) Extend(m goldmark.Markdown) { m.Parser().AddOptions(parser.WithBlockParsers( - util.Prioritized(ggcodeBlockParser{Preview: e.Preview}, 500), + util.Prioritized(ggcodeBlockParser{}, 500), )) m.Parser().AddOptions(parser.WithInlineParsers( - util.Prioritized(ggcodeInlineParser{Preview: e.Preview}, 500), + util.Prioritized(ggcodeInlineParser{}, 500), )) m.Renderer().AddOptions(renderer.WithNodeRenderers( - util.Prioritized(newGGCodeHTMLRenderer(), 500), + util.Prioritized(newGGCodeHTMLRenderer(e.Opts), 500), )) } diff --git a/src/parsing/parsing.go b/src/parsing/parsing.go index 67ea120..b2dae1e 100644 --- a/src/parsing/parsing.go +++ b/src/parsing/parsing.go @@ -133,11 +133,10 @@ func makeGoldmarkExtensions(opts MarkdownOptions) []goldmark.Extender { extenders = append(extenders, MathjaxExtension{}, BBCodeExtension{ - Preview: opts.Previews, - Education: opts.Education, + Preview: opts.Previews, }, ggcodeExtension{ - Preview: opts.Previews, + Opts: opts, }, ) diff --git a/src/website/education.go b/src/website/education.go index ad69ea2..c27d22f 100644 --- a/src/website/education.go +++ b/src/website/education.go @@ -347,9 +347,8 @@ func getEduArticleFromForm(form url.Values) (art models.EduArticle, ver models.E } art.Published = form.Get("published") != "" - // TODO: Education-specific Markdown ver.ContentRaw = form.Get("body") - ver.ContentHTML = parsing.ParseMarkdown(ver.ContentRaw, parsing.ForumRealMarkdown) + ver.ContentHTML = parsing.ParseMarkdown(ver.ContentRaw, parsing.EducationRealMarkdown) return }