修复文章发布报错

This commit is contained in:
2026-08-20 01:11:43 +08:00
parent 14f2e8b5c1
commit 758f072494
17 changed files with 644 additions and 69 deletions
+34 -21
View File
@@ -42,8 +42,10 @@ func RegisterSelfClose(name string, h SelfCloseHandler) { selfCloseHandlers[name
func RegisterGlobal(p GlobalProvider) { globalProvider = p }
var (
// 最内层块标签(内部不再嵌套块标签),反复匹配直至收敛
reBlock = regexp.MustCompile(`(?s)\{yz:([a-zA-Z]+)((?:\s+[a-zA-Z_][\w-]*\s*=\s*"[^"]*")*)\s*\}((?:(?!\{/?yz:).)*?)\{/yz:\1\}`)
// 块标签开标签(带属性);自闭合标签因 "/" 不会被匹配
reBlockOpen = regexp.MustCompile(`\{yz:([a-zA-Z]+)((?:\s+[a-zA-Z_][\w-]*\s*=\s*"[^"]*")*)\s*\}`)
// 块标签闭标签
reBlockClose = regexp.MustCompile(`\{/yz:([a-zA-Z]+)\}`)
// 自闭合标签 {yz:name attr="v" /}
reSelfClose = regexp.MustCompile(`\{yz:([a-zA-Z]+)((?:\s+[a-zA-Z_][\w-]*\s*=\s*"[^"]*")*)\s*/\}`)
// 全局标签 {yz:word}
@@ -122,28 +124,40 @@ func expandIncludes(dir, content string, depth int) (string, error) {
return out, nil
}
// renderBlocks 反复解析最内层块标签直至全部展开
// renderBlocks 逐个解析块标签:每次取全文第一个闭标签(必属最内层块),
// 向前回溯同名开标签,按行展开后回填,重复至无块标签(Go RE2 不支持负向先行断言,故手工配对)
func renderBlocks(content string, ctx *RenderCtx) (string, error) {
out := content
for i := 0; i < 10; i++ {
if !reBlock.MatchString(out) {
for i := 0; i < 100; i++ {
c := reBlockClose.FindStringIndex(out)
if c == nil {
break
}
var ferr error
out = reBlock.ReplaceAllStringFunc(out, func(m string) string {
sub := reBlock.FindStringSubmatch(m)
name := strings.ToLower(sub[1])
attrs := parseAttrs(sub[2])
body := sub[3]
p, ok := blockProviders[name]
if !ok {
return "" // 未注册的块标签直接置空,避免标签泄漏到前台
closeName := strings.ToLower(reBlockClose.FindStringSubmatch(out[c[0]:c[1]])[1])
// 在闭标签之前找最后一个同名开标签
openIdx, openEnd := -1, -1
attrs := ""
for _, om := range reBlockOpen.FindAllStringSubmatchIndex(out[:c[0]], -1) {
if strings.ToLower(out[om[2]:om[3]]) != closeName {
continue
}
rows, err := p(ctx.Tid, attrs, ctx)
openIdx, openEnd = om[0], om[1]
attrs = out[om[4]:om[5]]
}
if openIdx < 0 {
// 孤立的闭标签:移除后继续,避免死循环
out = out[:c[0]] + out[c[1]:]
continue
}
rendered := ""
if p, ok := blockProviders[closeName]; ok {
rows, err := p(ctx.Tid, parseAttrs(attrs), ctx)
if err != nil {
ferr = err
return ""
return "", err
}
body := out[openEnd:c[0]]
var sb strings.Builder
for _, row := range rows {
sb.WriteString(reField.ReplaceAllStringFunc(body, func(fm string) string {
@@ -151,11 +165,10 @@ func renderBlocks(content string, ctx *RenderCtx) (string, error) {
return row[field]
}))
}
return sb.String()
})
if ferr != nil {
return "", ferr
rendered = sb.String()
}
// 未注册的块标签置空,避免标签泄漏到前台
out = out[:openIdx] + rendered + out[c[1]:]
}
return out, nil
}