如何在Golang中转换Markdown为HTML并添加section标签
实现gomarkdown生成嵌套section包裹标题的方案
要实现类似markdown-it-header-sections的效果,核心是通过自定义AST(抽象语法树)转换,将标题及后续内容包裹到对应层级的<section>节点中。以下是具体实现步骤:
1. 核心思路
利用gomarkdown的AST遍历能力,维护一个层级栈来管理当前的<section>嵌套关系:
- 遇到标题节点时,创建对应ID的
<section>节点,将标题移入该section - 遇到非标题节点时,将其加入当前最内层的section中
- 通过标题层级(h2、h3等)调整栈的层级,确保section嵌套正确
2. 完整代码示例
import ( "bytes" "fmt" "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/ast" "github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/parser" ) // 遍历AST,将标题包裹进对应层级的section func wrapHeadingsInSections(doc ast.Node) ast.Node { sectionStack := []*ast.ElementNode{} ast.Walk(doc, func(node ast.Node, entering bool) ast.WalkStatus { if !entering { return ast.WalkContinue } // 处理标题节点 heading, isHeading := node.(*ast.Heading) if isHeading { // 创建section节点 section := ast.NewElementNode("section") // 复用AutoHeadingIDs生成的ID if idAttr, exists := heading.AttributeString("id"); exists { section.SetAttribute("id", idAttr) } // 将标题移入section section.AppendChild(section, heading) heading.Parent.RemoveChild(heading.Parent, heading) // 调整section层级栈:弹出比当前标题层级高或相等的section for len(sectionStack) > 0 { parentHeading := sectionStack[len(sectionStack)-1].FirstChild.(*ast.Heading) if parentHeading.Level < heading.Level { break } sectionStack = sectionStack[:len(sectionStack)-1] } // 将当前section加入父节点 if len(sectionStack) == 0 { doc.AppendChild(doc, section) } else { sectionStack[len(sectionStack)-1].AppendChild(sectionStack[len(sectionStack)-1], section) } sectionStack = append(sectionStack, section) return ast.WalkContinue } // 处理非标题节点:加入当前最内层section if len(sectionStack) > 0 { currentSection := sectionStack[len(sectionStack)-1] currentSection.AppendChild(currentSection, node) node.Parent.RemoveChild(node.Parent, node) } return ast.WalkContinue }) return doc } func main() { mdContent := []byte(`## Hello ### This is a test message Ligisnfmkdfn`) // 初始化解析器,开启所需扩展 parserExt := parser.CommonExtensions | parser.AutoHeadingIDs p := parser.NewWithExtensions(parserExt) astDoc := p.Parse(mdContent) // 转换AST结构 processedDoc := wrapHeadingsInSections(astDoc) // 初始化HTML渲染器 rendererOpts := html.RendererOptions{Flags: html.CommonFlags} renderer := html.NewRenderer(rendererOpts) // 生成最终HTML htmlResult := markdown.Render(processedDoc, renderer) fmt.Println(string(htmlResult)) }
3. 代码说明
- 层级栈管理:通过栈记录当前活跃的section,确保子标题(如h3)被正确包裹在父标题(如h2)的section内
- 节点迁移:将原标题和内容节点从父节点移除,重新添加到对应section中,保证AST结构正确
- ID复用:直接使用
AutoHeadingIDs生成的标题ID作为section的ID,和需求输出一致
内容的提问来源于stack exchange,提问作者Giao Le
相关产品推荐
相关产品推荐

