custom tag command
This commit is contained in:
parent
d4dbec8a84
commit
87f599a01e
|
|
@ -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, ""
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
8
main.go
8
main.go
|
|
@ -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
|
||||||
|
|
|
||||||
17
parse.go
17
parse.go
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue