aboutsummaryrefslogtreecommitdiff
path: root/markdown
diff options
context:
space:
mode:
authorAnhgelus Morhtuuzh <william@herges.fr>2025-12-13 17:15:47 +0000
committerAnhgelus Morhtuuzh <william@herges.fr>2025-12-13 17:15:47 +0000
commit62fa3f77e8215fdaaf72ddb9df4162e0d65148da (patch)
treedbf20dca933996cf253fe2d110d8381b0c407b2d /markdown
parentae297bbd117835304b298e7d8a2f914111940e77 (diff)
parent48311424ba2eaac254864c008b6d18e8510f827d (diff)
Merge pull request '[Refactor] Replace manual DOM manipulation in markdown by cleaner one' (#2) from refactor/mardown-dom into main
Reviewed-on: https://git.anhgelus.world/anhgelus/small-web/pulls/2
Diffstat (limited to 'markdown')
-rw-r--r--markdown/ast.go9
-rw-r--r--markdown/ast_code.go12
-rw-r--r--markdown/ast_external.go19
-rw-r--r--markdown/ast_external_test.go29
-rw-r--r--markdown/ast_heading.go (renamed from markdown/ast_header.go)17
-rw-r--r--markdown/ast_list.go13
-rw-r--r--markdown/ast_modifier.go6
-rw-r--r--markdown/ast_paragraph.go10
-rw-r--r--markdown/ast_paragraph_test.go9
-rw-r--r--markdown/ast_quote.go19
-rw-r--r--markdown/ast_test.go9
-rw-r--r--markdown/lexer.go8
12 files changed, 81 insertions, 79 deletions
diff --git a/markdown/ast.go b/markdown/ast.go
index 9c9816e..f409ccb 100644
--- a/markdown/ast.go
+++ b/markdown/ast.go
@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"html/template"
- "strings"
)
var ErrUnkownLexType = errors.New("unkown lex type")
@@ -63,11 +62,11 @@ func getBlock(lxs *lexers, newLine bool) (block, *ParseError) {
var b block
var err *ParseError
switch lxs.Current().Type {
- case lexerHeader:
+ case lexerHeading:
if !newLine {
b, err = paragraph(lxs, false)
} else {
- b, err = header(lxs)
+ b, err = heading(lxs)
}
case lexerExternal:
if newLine && lxs.Current().Value == "![" {
@@ -107,7 +106,3 @@ func getBlock(lxs *lexers, newLine bool) (block, *ParseError) {
}
return b, err
}
-
-func trimSpace(s template.HTML) template.HTML {
- return template.HTML(strings.TrimSpace(string(s)))
-}
diff --git a/markdown/ast_code.go b/markdown/ast_code.go
index 6980f32..c83a984 100644
--- a/markdown/ast_code.go
+++ b/markdown/ast_code.go
@@ -2,8 +2,10 @@ package markdown
import (
"errors"
- "fmt"
+ "html"
"html/template"
+
+ "git.anhgelus.world/anhgelus/small-web/dom"
)
var (
@@ -26,11 +28,15 @@ type astCode struct {
}
func (a *astCode) Eval(_ *Option) (template.HTML, *ParseError) {
+ content := template.HTML(html.EscapeString(a.content))
switch a.codeType {
case codeOneLine:
- return template.HTML(fmt.Sprintf("<code>%s</code>", template.HTMLEscapeString(a.content))), nil
+ return dom.NewLiteralContentElement("code", content).Render(), nil
case codeMultiLine:
- return template.HTML(fmt.Sprintf("<pre><code>%s</code></pre>", template.HTMLEscapeString(a.content))), nil
+ pre := dom.NewContentElement("pre", make([]dom.Element, 1))
+ code := dom.NewContentElement("code", []dom.Element{dom.NewLiteralElement(content)})
+ pre.Contents[0] = code
+ return pre.Render(), nil
default:
return "", &ParseError{lxs: lexers{}, internal: ErrUnknownCodeType}
}
diff --git a/markdown/ast_external.go b/markdown/ast_external.go
index 8adeea1..4a1024a 100644
--- a/markdown/ast_external.go
+++ b/markdown/ast_external.go
@@ -1,9 +1,10 @@
package markdown
import (
- "fmt"
"html/template"
"regexp"
+
+ "git.anhgelus.world/anhgelus/small-web/dom"
)
var ExternalLink = regexp.MustCompile(`https?://`)
@@ -31,10 +32,12 @@ func (a *astLink) Eval(opt *Option) (template.HTML, *ParseError) {
}
func RenderLink(content, href string) template.HTML {
- if !ExternalLink.Match([]byte(href)) {
- return template.HTML(fmt.Sprintf(`<a href="%s">%s</a>`, href, content))
+ anchor := dom.NewLiteralContentElement("a", template.HTML(content))
+ anchor.SetAttribute("href", href)
+ if ExternalLink.MatchString(href) {
+ anchor.SetAttribute("target", "_blank").SetAttribute("rel", "noreferer")
}
- return template.HTML(fmt.Sprintf(`<a href="%s" target="_blank" rel="noreferer">%s</a>`, href, content))
+ return anchor.Render()
}
type astImage struct {
@@ -53,8 +56,10 @@ func (a *astImage) Eval(opt *Option) (template.HTML, *ParseError) {
return "", err
}
src = template.HTML(opt.ImageSource(string(src)))
+ img := dom.NewImg(string(src), string(alt))
+ figure := dom.NewContentElement("figure", []dom.Element{img})
if a.source == nil {
- return template.HTML(fmt.Sprintf(`<figure><img alt="%s" src="%s"></figure>`, alt, src)), nil
+ return figure.Render(), nil
}
var s template.HTML
for _, c := range a.source {
@@ -65,7 +70,9 @@ func (a *astImage) Eval(opt *Option) (template.HTML, *ParseError) {
s += ct + " "
}
s = s[:len(s)-1]
- return template.HTML(fmt.Sprintf(`<figure><img alt="%s" src="%s"><figcaption>%s</figcaption></figure>`, alt, src, s)), nil
+ figcaption := dom.NewLiteralContentElement("figcaption", s)
+ figure.Contents = append(figure.Contents, figcaption)
+ return figure.Render(), nil
}
func external(lxs *lexers) (block, *ParseError) {
diff --git a/markdown/ast_external_test.go b/markdown/ast_external_test.go
index 514cc89..c31c233 100644
--- a/markdown/ast_external_test.go
+++ b/markdown/ast_external_test.go
@@ -3,51 +3,34 @@ package markdown
import "testing"
func TestExternal(t *testing.T) {
- lxs := lex("[content](href)")
- tree, err := ast(lxs)
- if err != nil {
- t.Fatal(err)
- }
- got, err := tree.Eval(nil)
+ got, err := Parse("[content](href)", nil)
if err != nil {
t.Fatal(err)
}
if string(got) != `<p><a href="href">content</a></p>` {
t.Errorf("invalid value, got %s", got)
- t.Logf("lexer %s", lxs.String())
}
- lxs = lex("![image alt](image src)")
- tree, err = ast(lxs)
+ got, err = Parse("![image alt](image src)", nil)
if err != nil {
- t.Logf("lexer %s", lxs.String())
t.Fatal(err)
}
- got, err = tree.Eval(nil)
- if err != nil {
- t.Fatal(err)
- }
- if string(got) != `<figure><img alt="image alt" src="image src"></figure>` {
+ if string(got) != `<figure><img alt="image alt" src="image src" /></figure>` {
t.Errorf("invalid value, got %s", got)
}
- lxs = lex(`
+ got, err = Parse(`
Avant la source
![image alt](image src)
source 1
source 2
Hors de la source
-`)
- tree, err = ast(lxs)
- if err != nil {
- t.Fatal(err)
- }
- got, err = tree.Eval(nil)
+`, nil)
if err != nil {
t.Fatal(err)
}
- if string(got) != `<p>Avant la source</p><figure><img alt="image alt" src="image src"><figcaption>source 1 source 2</figcaption></figure><p>Hors de la source</p>` {
+ if string(got) != `<p>Avant la source</p><figure><img alt="image alt" src="image src" /><figcaption>source 1 source 2</figcaption></figure><p>Hors de la source</p>` {
t.Errorf("invalid value, got %s", got)
}
diff --git a/markdown/ast_header.go b/markdown/ast_heading.go
index 716a4a6..206ff94 100644
--- a/markdown/ast_header.go
+++ b/markdown/ast_heading.go
@@ -2,18 +2,20 @@ package markdown
import (
"errors"
- "fmt"
"html/template"
+ "strings"
+
+ "git.anhgelus.world/anhgelus/small-web/dom"
)
var ErrInvalidHeader = errors.New("invalid header")
-type astHeader struct {
+type astHeading struct {
level uint
content *astParagraph
}
-func (a *astHeader) Eval(opt *Option) (template.HTML, *ParseError) {
+func (a *astHeading) Eval(opt *Option) (template.HTML, *ParseError) {
if a.level > 6 {
return "", &ParseError{lxs: lexers{}, internal: ErrInvalidCodeFormat}
}
@@ -22,11 +24,14 @@ func (a *astHeader) Eval(opt *Option) (template.HTML, *ParseError) {
if err != nil {
return "", err
}
- return template.HTML(fmt.Sprintf("<h%d>%s</h%d>", a.level, trimSpace(content), a.level)), nil
+ return dom.NewHeading(
+ a.level,
+ template.HTML(strings.TrimSpace(string(content))),
+ ).Render(), nil
}
-func header(lxs *lexers) (*astHeader, *ParseError) {
- b := &astHeader{level: uint(len(lxs.Current().Value))}
+func heading(lxs *lexers) (*astHeading, *ParseError) {
+ b := &astHeading{level: uint(len(lxs.Current().Value))}
if !lxs.Next() {
return nil, &ParseError{lxs: *lxs, internal: ErrInvalidHeader}
}
diff --git a/markdown/ast_list.go b/markdown/ast_list.go
index b82df67..b07f013 100644
--- a/markdown/ast_list.go
+++ b/markdown/ast_list.go
@@ -1,9 +1,11 @@
package markdown
import (
- "fmt"
"html/template"
"regexp"
+ "strings"
+
+ "git.anhgelus.world/anhgelus/small-web/dom"
)
var regexOrdered = regexp.MustCompile(`\d+\.`)
@@ -21,15 +23,18 @@ type astList struct {
}
func (a *astList) Eval(opt *Option) (template.HTML, *ParseError) {
- var content template.HTML
+ list := dom.NewContentElement(string(a.tag), make([]dom.Element, 0))
for _, c := range a.content {
ct, err := c.Eval(opt)
if err != nil {
return "", err
}
- content += template.HTML(fmt.Sprintf("<li>%s</li>", trimSpace(ct)))
+ list.Contents = append(list.Contents, dom.NewLiteralContentElement(
+ "li",
+ template.HTML(strings.TrimSpace(string(ct))),
+ ))
}
- return template.HTML(fmt.Sprintf("<%s>%s</%s>", a.tag, content, a.tag)), nil
+ return list.Render(), nil
}
func list(lxs *lexers) (block, *ParseError) {
diff --git a/markdown/ast_modifier.go b/markdown/ast_modifier.go
index 909d25e..7672c0c 100644
--- a/markdown/ast_modifier.go
+++ b/markdown/ast_modifier.go
@@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"html/template"
+
+ "git.anhgelus.world/anhgelus/small-web/dom"
)
var (
@@ -38,7 +40,7 @@ func (a *astModifier) Eval(opt *Option) (template.HTML, *ParseError) {
if a.super {
return content, nil
}
- return template.HTML(fmt.Sprintf("<%s>%s</%s>", a.tag, content, a.tag)), nil
+ return dom.NewLiteralContentElement(string(a.tag), content).Render(), nil
}
func (a *astModifier) String() string {
@@ -66,7 +68,7 @@ func modifier(lxs *lexers) (*astModifier, error) {
var s string
for lxs.Next() {
switch lxs.Current().Type {
- case lexerLiteral, lexerHeader, lexerList:
+ case lexerLiteral, lexerHeading, lexerList:
s += lxs.Current().Value
case lexerModifier:
if mod.super && []rune(mod.symbols)[0] == []rune(lxs.Current().Value)[0] &&
diff --git a/markdown/ast_paragraph.go b/markdown/ast_paragraph.go
index a6417e2..eddba0a 100644
--- a/markdown/ast_paragraph.go
+++ b/markdown/ast_paragraph.go
@@ -2,8 +2,10 @@ package markdown
import (
"errors"
- "fmt"
"html/template"
+ "strings"
+
+ "git.anhgelus.world/anhgelus/small-web/dom"
)
var (
@@ -27,7 +29,9 @@ func (a *astParagraph) Eval(opt *Option) (template.HTML, *ParseError) {
if a.oneLine {
return content, nil
}
- return template.HTML(fmt.Sprintf("<p>%s</p>", trimSpace(content))), nil
+ return dom.NewParagraph(
+ template.HTML(strings.TrimSpace(string(content))),
+ ).Render(), nil
}
func paragraph(lxs *lexers, oneLine bool) (*astParagraph, *ParseError) {
@@ -49,7 +53,7 @@ func paragraph(lxs *lexers, oneLine bool) (*astParagraph, *ParseError) {
return tree, nil
}
tree.content = append(tree.content, astLiteral(lxs.Current().Value))
- case lexerLiteral, lexerHeader:
+ case lexerLiteral, lexerHeading:
s := lxs.Current().Value
// replace line break by space
if n > 0 && len(tree.content) != 0 {
diff --git a/markdown/ast_paragraph_test.go b/markdown/ast_paragraph_test.go
index 9155a9b..46dd714 100644
--- a/markdown/ast_paragraph_test.go
+++ b/markdown/ast_paragraph_test.go
@@ -3,18 +3,11 @@ package markdown
import "testing"
func TestParagraph(t *testing.T) {
- content := "bonsoir"
- lxs := lex(content)
- tree, err := ast(lxs)
- if err != nil {
- t.Fatal(err)
- }
- c, err := tree.Eval(nil)
+ c, err := Parse("bonsoir", nil)
if err != nil {
t.Fatal(err)
}
if c != "<p>bonsoir</p>" {
t.Errorf("failed, got %s", c)
- t.Logf("lxs: %s\ntree: %s", lxs, tree)
}
}
diff --git a/markdown/ast_quote.go b/markdown/ast_quote.go
index fa3a878..96669c5 100644
--- a/markdown/ast_quote.go
+++ b/markdown/ast_quote.go
@@ -1,9 +1,10 @@
package markdown
import (
- "fmt"
"html/template"
"strings"
+
+ "git.anhgelus.world/anhgelus/small-web/dom"
)
type astQuote struct {
@@ -12,15 +13,18 @@ type astQuote struct {
}
func (a *astQuote) Eval(opt *Option) (template.HTML, *ParseError) {
- var quote template.HTML
+ var quoteContent template.HTML
for _, c := range a.quote {
ct, err := c.Eval(opt)
if err != nil {
return "", err
}
- quote += ct
+ quoteContent += ct
}
- quote = template.HTML(fmt.Sprintf("<blockquote>%s</blockquote>", trimSpace(quote)))
+ blockquote := dom.NewLiteralContentElement(
+ "blockquote",
+ template.HTML(strings.TrimSpace(string(quoteContent))),
+ )
var source template.HTML
for _, c := range a.source {
ct, err := c.Eval(opt)
@@ -30,10 +34,13 @@ func (a *astQuote) Eval(opt *Option) (template.HTML, *ParseError) {
source += ct
}
source = template.HTML(strings.TrimSpace(string(source)))
+ quote := dom.NewContentElement("div", make([]dom.Element, 0))
+ quote.ClassList().Add("quote")
+ quote.Contents = append(quote.Contents, blockquote)
if len(source) > 0 {
- return template.HTML(fmt.Sprintf(`<div class="quote">%s<p>%s</p></div>`, quote, source)), nil
+ quote.Contents = append(quote.Contents, dom.NewParagraph(source))
}
- return template.HTML(fmt.Sprintf(`<div class="quote">%s</div>`, quote)), nil
+ return quote.Render(), nil
}
func quote(lxs *lexers) (*astQuote, *ParseError) {
diff --git a/markdown/ast_test.go b/markdown/ast_test.go
index 45eb49d..8f701f4 100644
--- a/markdown/ast_test.go
+++ b/markdown/ast_test.go
@@ -40,18 +40,13 @@ var parsed = `
<ol><li>et maintenant</li><li>elle l&#39;est</li></ol>
<ul><li>hehe</li></ul>
<figure>
-<img alt="Ceci est ma pfp :3" src="https://cdn.anhgelus.world/pfp.jpg">
+<img alt="Ceci est ma pfp :3" src="https://cdn.anhgelus.world/pfp.jpg" />
<figcaption> <a href="https://now.anhgelus.world/" target="_blank" rel="noreferer">Ma pfp</a> hehe :D Elle est <b>magnifique</b>, n&#39;est-ce pas ?</figcaption>
</figure>
`
func TestAst(t *testing.T) {
- lxs := lex(raw)
- tree, err := ast(lxs)
- if err != nil {
- t.Fatal(err)
- }
- res, err := tree.Eval(nil)
+ res, err := Parse(raw, nil)
if err != nil {
t.Fatal(err)
}
diff --git a/markdown/lexer.go b/markdown/lexer.go
index 30587e4..b68bbf9 100644
--- a/markdown/lexer.go
+++ b/markdown/lexer.go
@@ -14,9 +14,9 @@ const (
lexerCode lexerType = "code"
- lexerHeader lexerType = "header"
- lexerQuote lexerType = "quote"
- lexerList lexerType = "list"
+ lexerHeading lexerType = "header"
+ lexerQuote lexerType = "quote"
+ lexerList lexerType = "list"
lexerExternal lexerType = "external"
@@ -115,7 +115,7 @@ func lex(s string) *lexers {
fn(c, lexerBreak, nil)
case '#':
newLine = false
- fn(c, lexerHeader, nil)
+ fn(c, lexerHeading, nil)
case '>':
newLine = false
fn(c, lexerQuote, nil)