custom tag command

This commit is contained in:
root 2026-02-13 08:49:17 +00:00
parent d4dbec8a84
commit 87f599a01e
5 changed files with 179 additions and 3 deletions

148
cmd_addtag.go Normal file
View File

@ -0,0 +1,148 @@
package main
import (
"fmt"
"os"
"strings"
"unicode"
)
// AddTag generates code for a new custom tag
func AddTag(delimiter, tagName, targetElement string) {
fmt.Printf("Adding tag: %s -> %s (delimiter: %s)\n", tagName, targetElement, delimiter)
if err := updateTagsFile(delimiter, tagName); err != nil {
fmt.Printf("Error updating templates/tags.go: %v\n", err)
os.Exit(1)
}
if err := updateParseFile(tagName, targetElement); err != nil {
fmt.Printf("Error updating parse.go: %v\n", err)
os.Exit(1)
}
fmt.Println("Successfully added tag!")
}
func updateTagsFile(delimiter, tagName string) error {
path := "templates/tags.go"
content, err := os.ReadFile(path)
if err != nil {
return err
}
pascalTagName := toPascalCase(tagName)
newLine := fmt.Sprintf("var %s = customtag.New(\"%s\", \"%s\")\n", pascalTagName, delimiter, tagName)
// check if already exists
if strings.Contains(string(content), newLine) {
fmt.Println("Tag definition already exists in templates/tags.go")
return nil
}
// Append to file
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
if _, err := f.WriteString(newLine); err != nil {
return err
}
return nil
}
func updateParseFile(tagName, targetElement string) error {
path := "parse.go"
contentBytes, err := os.ReadFile(path)
if err != nil {
return err
}
content := string(contentBytes)
pascalTagName := toPascalCase(tagName)
// 1. Add to Goldmark Extensions
// Find `templates.SidebarTag,` or `templates.TopBanner,` and add after it
// We'll look for `goldmark.WithExtensions(` and find the closing `),`
extLine := fmt.Sprintf("\t\t\t\ttemplates.%s,", pascalTagName)
if !strings.Contains(content, extLine) {
// Simple insertion strategy: find `templates.TopBanner,`
anchor := "templates.TopBanner,"
if idx := strings.Index(content, anchor); idx != -1 {
insertAt := idx + len(anchor)
content = content[:insertAt] + "\n" + extLine + content[insertAt:]
} else {
return fmt.Errorf("could not find anchor 'templates.TopBanner,' in parse.go to insert extension registration")
}
} else {
fmt.Println("Extension registration already exists in parse.go")
}
// 2. Add Post-Processing Logic
// We'll look for `// Build Full Page` and insert before it, or after the TopBanner block.
// A good anchor is `// _-_- TopBanner` block's end.
// Let's construct the code block to insert
targetTag, targetClass := parseTargetElement(targetElement)
// Prepare the replacement logic
var replacementLogic string
if targetClass != "" {
replacementLogic = fmt.Sprintf(`return fmt.Sprintf("<%s class=\"%s\">%%s</%s>", innerContent)`, targetTag, targetClass, targetTag)
} else {
replacementLogic = fmt.Sprintf(`return fmt.Sprintf("<%s>%%s</%s>", innerContent)`, targetTag, targetTag)
}
codeBlock := fmt.Sprintf(`
// %s %s
%sRegex := regexp.MustCompile("(?s)<%s>.*?</%s>")
if %sRegex.MatchString(htmlContent) {
htmlContent = %sRegex.ReplaceAllStringFunc(htmlContent, func(match string) string {
innerContent := strings.TrimPrefix(match, "<%s>")
innerContent = strings.TrimSuffix(innerContent, "</%s>")
%s
})
}
`, pascalTagName, tagName, tagName, tagName, tagName, tagName, tagName, tagName, tagName, replacementLogic)
if !strings.Contains(content, fmt.Sprintf("%sRegex :=", tagName)) {
// Anchor: find end of TopBanner block
// We know TopBanner block ends with a closing brace for the `if topbannerRegex.MatchString` block
// But strictly speaking, we are inside `if file.RoutePath == "/" { ... }`.
// Let's look for `// Build Full Page` and insert BEFORE it.
anchor := "// Build Full Page"
if idx := strings.Index(content, anchor); idx != -1 {
// Insert BEFORE "// Build Full Page", which matches the global scope (after the if block)
content = content[:idx] + codeBlock + content[idx:]
} else {
return fmt.Errorf("could not find anchor '// Build Full Page' in parse.go")
}
} else {
fmt.Println("Regex logic already exists in parse.go")
}
return os.WriteFile(path, []byte(content), 0644)
}
func toPascalCase(s string) string {
if s == "" {
return ""
}
runes := []rune(s)
runes[0] = unicode.ToUpper(runes[0])
return string(runes)
}
func parseTargetElement(t string) (string, string) {
parts := strings.Split(t, ".")
if len(parts) == 2 {
return parts[0], parts[1]
}
return t, ""
}

View File

@ -1 +1,7 @@
# I'm quite fond of lyrics. Particicularly, I like interpol lyrics. # I'm quite fond of lyrics. Particicularly, I like interpol lyrics.
I'm testing custom tags:
{[]}(\(x+x\)).
{[]}x+1

View File

@ -19,6 +19,14 @@ func main() {
ClearCache() ClearCache()
StartServer() StartServer()
return return
case "addTag":
if len(os.Args) < 5 {
fmt.Println("Usage: blog addTag {delimiter} {tagName} {targetElement}")
fmt.Println("Example: blog addTag {[]} mathtext li.mathtext")
return
}
AddTag(os.Args[2], os.Args[3], os.Args[4])
return
default: default:
fmt.Printf("Unknown command: %s\nUsage: blog [service|test]\n", os.Args[1]) fmt.Printf("Unknown command: %s\nUsage: blog [service|test]\n", os.Args[1])
return return

View File

@ -61,6 +61,7 @@ func GetContentPath(file ContentFile) (string, error) {
&frontmatter.Extender{}, &frontmatter.Extender{},
templates.SidebarTag, // ||| -> <sidebar> templates.SidebarTag, // ||| -> <sidebar>
templates.TopBanner, templates.TopBanner,
templates.Mathtext,
), ),
goldmark.WithRendererOptions( goldmark.WithRendererOptions(
html.WithUnsafe(), html.WithUnsafe(),
@ -143,9 +144,21 @@ func GetContentPath(file ContentFile) (string, error) {
return fmt.Sprintf(`<div class="topbanner">%s</div>`, bannerHTML) return fmt.Sprintf(`<div class="topbanner">%s</div>`, bannerHTML)
}) })
} }
}
// Build Full Page
}
// Mathtext mathtext
mathtextRegex := regexp.MustCompile("(?s)<mathtext>.*?</mathtext>")
if mathtextRegex.MatchString(htmlContent) {
htmlContent = mathtextRegex.ReplaceAllStringFunc(htmlContent, func(match string) string {
innerContent := strings.TrimPrefix(match, "<mathtext>")
innerContent = strings.TrimSuffix(innerContent, "</mathtext>")
return fmt.Sprintf("<li class=\"mathtext\">%s</li>", innerContent)
})
}
// Build Full Page
dataToWrite = templates.BuildFullPage([]byte(htmlContent), meta) dataToWrite = templates.BuildFullPage([]byte(htmlContent), meta)
} }

View File

@ -6,3 +6,4 @@ import customtag "github.com/tendstofortytwo/goldmark-customtag"
// ||| -> <sidebar></sidebar> // ||| -> <sidebar></sidebar>
var SidebarTag = customtag.New("|||", "sidebar") var SidebarTag = customtag.New("|||", "sidebar")
var TopBanner = customtag.New("_-_-", "topbanner") var TopBanner = customtag.New("_-_-", "topbanner")
var Mathtext = customtag.New("{[]}", "mathtext")