aboutsummaryrefslogtreecommitdiff
path: root/markdown/ast_external.go
diff options
context:
space:
mode:
authorAnhgelus Morhtuuzh <william@herges.fr>2025-10-02 19:52:38 +0200
committerAnhgelus Morhtuuzh <william@herges.fr>2025-10-02 19:52:38 +0200
commit94dceb4f7c1740de9215b36ec183f93ca4337ee7 (patch)
tree5ad184efb0b74dd3aa4da7585f88a3e3f6cb4ecc /markdown/ast_external.go
parent8b249c9ce8bd1c351daf80c6c9b095fb1bccafe2 (diff)
style(markdown): fix typo in package name
Diffstat (limited to 'markdown/ast_external.go')
-rw-r--r--markdown/ast_external.go179
1 files changed, 179 insertions, 0 deletions
diff --git a/markdown/ast_external.go b/markdown/ast_external.go
new file mode 100644
index 0000000..a172b2b
--- /dev/null
+++ b/markdown/ast_external.go
@@ -0,0 +1,179 @@
+package markdown
+
+import (
+ "fmt"
+ "html/template"
+)
+
+type astLink struct {
+ content block
+ href block
+}
+
+func (a *astLink) Eval() (template.HTML, *ParseError) {
+ content, err := a.content.Eval()
+ if err != nil {
+ return "", err
+ }
+ href, err := a.href.Eval()
+ if err != nil {
+ return "", err
+ }
+ return template.HTML(fmt.Sprintf(`<a href="%s">%s</a>`, href, content)), nil
+}
+
+type astImage struct {
+ alt block
+ src block
+ source []*astParagraph
+}
+
+func (a *astImage) Eval() (template.HTML, *ParseError) {
+ alt, err := a.alt.Eval()
+ if err != nil {
+ return "", err
+ }
+ src, err := a.src.Eval()
+ if err != nil {
+ return "", err
+ }
+ if a.source == nil {
+ return template.HTML(fmt.Sprintf(`<figure><img alt="%s" src="%s"></figure>`, alt, src)), nil
+ }
+ var s template.HTML
+ for _, c := range a.source {
+ ct, err := c.Eval()
+ if err != nil {
+ return "", err
+ }
+ 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
+}
+
+func external(lxs *lexers) (block, *ParseError) {
+ tp := lxs.Current().Value
+ if !lxs.Next() {
+ return astLiteral(tp), nil
+ }
+ lxs.Before() // because we call Next
+ var b block
+ var err *ParseError
+ switch tp {
+ case "![":
+ b, err = image(lxs)
+ case "[":
+ b, err = link(lxs)
+ default:
+ b = astLiteral(tp)
+ }
+ return b, err
+}
+
+func link(lxs *lexers) (block, *ParseError) {
+ lk := new(astLink)
+ start := lxs.current
+ content, href, _, ok := parseExternal(lxs, false)
+ if !ok {
+ return reset(lxs, start), nil
+ }
+ lk.content = astLiteral(content)
+ lk.href = astLiteral(href)
+ return lk, nil
+}
+
+func image(lxs *lexers) (block, *ParseError) {
+ img := new(astImage)
+ start := lxs.current
+ alt, src, source, ok := parseExternal(lxs, true)
+ if !ok {
+ return reset(lxs, start), nil
+ }
+ img.alt = astLiteral(alt)
+ img.src = astLiteral(src)
+ img.source = source
+ return img, nil
+}
+
+func parseExternal(lxs *lexers, withSource bool) (string, string, []*astParagraph, bool) {
+ next := false
+ var s string
+ var first string
+ var end string
+ var ps []*astParagraph
+ n := 0
+ fn := func() bool {
+ p, err := paragraph(lxs, true)
+ if err != nil {
+ return false
+ }
+ ps = append(ps, p)
+ n = 0
+ return true
+ }
+ for lxs.Next() && n < 2 {
+ switch lxs.Current().Type {
+ case lexerBreak:
+ if !withSource {
+ return "", "", nil, false
+ }
+ n += len(lxs.Current().Value)
+ if first != "" && end != "" {
+ if !lxs.Next() {
+ return first, end, ps, true
+ }
+ ok := fn()
+ if !ok {
+ return "", "", nil, false
+ }
+ lxs.Before() // because we must parse lexerBreak
+ }
+ case lexerExternal:
+ if first != "" && end != "" {
+ return "", "", nil, false
+ }
+ if n > 0 && (first == "" || end == "") {
+ return "", "", nil, false
+ }
+ n = 0
+ if !next {
+ if lxs.Current().Value != "](" || !lxs.Next() {
+ return "", "", nil, false
+ }
+ lxs.Before() // because we called Next
+ first = s
+ s = ""
+ next = true
+ } else {
+ if lxs.Current().Value != ")" {
+ return "", "", nil, false
+ }
+ if !withSource {
+ return first, s, nil, true
+ }
+ end = s
+ s = ""
+ if lxs.Next() && lxs.Current().Type != lexerBreak {
+ return "", "", nil, false
+ }
+ lxs.Before() // because we called Next
+ }
+ default:
+ if ps != nil {
+ return "", "", nil, false
+ }
+ n = 0
+ s += lxs.Current().Value
+ }
+ }
+ if !withSource {
+ return "", "", nil, false
+ }
+ return first, end, ps, true
+}
+
+func reset(lxs *lexers, start int) block {
+ lxs.current = start
+ return astLiteral(lxs.Current().Value)
+}