aboutsummaryrefslogtreecommitdiff
path: root/markdown/ast_modifier.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_modifier.go
parent8b249c9ce8bd1c351daf80c6c9b095fb1bccafe2 (diff)
style(markdown): fix typo in package name
Diffstat (limited to 'markdown/ast_modifier.go')
-rw-r--r--markdown/ast_modifier.go150
1 files changed, 150 insertions, 0 deletions
diff --git a/markdown/ast_modifier.go b/markdown/ast_modifier.go
new file mode 100644
index 0000000..205786a
--- /dev/null
+++ b/markdown/ast_modifier.go
@@ -0,0 +1,150 @@
+package markdown
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "html/template"
+)
+
+var (
+ ErrInvalidUsage = errors.Join(ErrInvalidParagraph, errors.New("invalid modifier usage"))
+ ErrInvalidTypeInModifier = errors.Join(ErrInvalidParagraph, errors.New("invalid type in modifier"))
+)
+
+type modifierTag string
+
+const (
+ boldTag modifierTag = "b"
+ emTag modifierTag = "em"
+)
+
+type astModifier struct {
+ symbols string
+ tag modifierTag
+ content []block
+ super bool
+}
+
+func (a *astModifier) Eval() (template.HTML, *ParseError) {
+ var content template.HTML
+ for _, c := range a.content {
+ ct, err := c.Eval()
+ if err != nil {
+ return "", &ParseError{lxs: lexers{}, internal: err}
+ }
+ content += ct
+ }
+ if a.super {
+ return content, nil
+ }
+ return template.HTML(fmt.Sprintf("<%s>%s</%s>", a.tag, content, a.tag)), nil
+}
+
+func (a *astModifier) String() string {
+ content := "["
+ for _, c := range a.content {
+ content += "\n\t"
+ if v, ok := c.(fmt.Stringer); ok {
+ content += v.String()
+ } else {
+ b, _ := json.MarshalIndent(a.content, "\t", " ")
+ content += string(b)
+ }
+ content += ",\n\t"
+ }
+ content += "]"
+ return fmt.Sprintf("modifier{sym: %s, tag: %s, super: %v, content: %s\n}", a.symbols, a.tag, a.super, content)
+}
+
+func modifier(lxs *lexers) (*astModifier, error) {
+ current := lxs.Current().Value
+ mod, err := modifierDetect(current)
+ if err != nil {
+ return nil, err
+ }
+ var s string
+ for lxs.Next() {
+ switch lxs.Current().Type {
+ case lexerLiteral, lexerHeader, lexerList:
+ s += lxs.Current().Value
+ case lexerModifier:
+ if mod.super && []rune(mod.symbols)[0] == []rune(lxs.Current().Value)[0] &&
+ len(mod.symbols) >= len(lxs.Current().Value) {
+ mod.symbols = mod.symbols[len(lxs.Current().Value):]
+ subMod, err := modifierDetect(lxs.Current().Value)
+ if err != nil {
+ return nil, err
+ }
+ if !subMod.super {
+ subMod.content = append(subMod.content, astLiteral(s))
+ mod, err = modifierDetect(mod.symbols) // this trick is so cool :D
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ subMod, _ = modifierDetect("**")
+ subEm, _ := modifierDetect("*")
+ subEm.content = append(subEm.content, astLiteral(s))
+ subMod.content = append(subMod.content, subEm)
+ }
+ s = ""
+ mod.content = append(mod.content, subMod)
+ if len(mod.symbols) == 0 {
+ return mod, nil
+ }
+ } else {
+ if lxs.Current().Value == mod.symbols {
+ mod.content = append(mod.content, astLiteral(s))
+ return mod, nil
+ } else if len(s) != 0 {
+ mod.content = append(mod.content, astLiteral(s))
+ s = ""
+ }
+ c, err := modifier(lxs)
+ if err != nil {
+ return nil, err
+ }
+ mod.content = append(mod.content, c)
+ }
+ case lexerBreak:
+ lxs.Before() // because we did not use it
+ if len(s) != 0 {
+ return nil, ErrInvalidUsage
+ }
+ return mod, nil
+ case lexerExternal:
+ if lxs.Current().Value == "!" {
+ s += lxs.Current().Value
+ } else {
+ ext, err := external(lxs)
+ if err != nil {
+ return nil, err
+ }
+ mod.content = append(mod.content, ext)
+ }
+ default:
+ return nil, ErrInvalidTypeInModifier
+ }
+ }
+ if len(s) != 0 {
+ return nil, ErrInvalidUsage
+ }
+ return mod, nil
+}
+
+func modifierDetect(val string) (*astModifier, error) {
+ mod := new(astModifier)
+ mod.symbols = val
+ switch len(val) {
+ case 1:
+ mod.tag = emTag
+ case 2:
+ mod.tag = boldTag
+ case 3:
+ mod.super = true
+ default:
+ return nil, ErrInvalidUsage
+ }
+ return mod, nil
+}