@@ -3,43 +3,40 @@ package markdown
33import (
44 "fmt"
55 "strings"
6- "sync"
76
87 "github.com/yuin/goldmark/ast"
98 "github.com/yuin/goldmark/parser"
109 "github.com/yuin/goldmark/text"
1110)
1211
13- // headingIDTransformer 为标题自动生成唯一 ID 属性
14- type headingIDTransformer struct {
15- usedIDs map [string ]int
16- mu sync.Mutex
17- }
12+ // headingIDTransformer 为标题自动生成唯一 ID 属性.
13+ // It is safe for concurrent use: each Transform call uses a local map so
14+ // multiple documents can be parsed in parallel without interference.
15+ type headingIDTransformer struct {}
1816
1917func newHeadingIDTransformer () parser.ASTTransformer {
20- return & headingIDTransformer {
21- usedIDs : make (map [string ]int ),
22- }
18+ return & headingIDTransformer {}
2319}
2420
25- // Transform 遍历 AST,为所有标题节点生成 ID
21+ // Transform 遍历 AST,为所有标题节点生成 ID.
22+ // A fresh usedIDs map is created per call so heading IDs are scoped to a
23+ // single document and concurrent Transform calls don't interfere.
2624func (t * headingIDTransformer ) Transform (node * ast.Document , reader text.Reader , pc parser.Context ) {
25+ usedIDs := make (map [string ]int )
2726 source := reader .Source ()
28- if err : = ast .Walk (node , func (n ast.Node , entering bool ) (ast.WalkStatus , error ) {
27+ _ = ast .Walk (node , func (n ast.Node , entering bool ) (ast.WalkStatus , error ) {
2928 if ! entering {
3029 return ast .WalkContinue , nil
3130 }
3231 if heading , ok := n .(* ast.Heading ); ok {
33- t . processHeading (heading , source )
32+ processHeading (heading , source , usedIDs )
3433 }
3534 return ast .WalkContinue , nil
36- }); err != nil {
37- return
38- }
35+ })
3936}
4037
4138// processHeading 为单个标题节点设置 ID 属性
42- func ( t * headingIDTransformer ) processHeading (heading * ast.Heading , source []byte ) {
39+ func processHeading (heading * ast.Heading , source []byte , usedIDs map [ string ] int ) {
4340 if _ , ok := heading .AttributeString ("id" ); ok {
4441 return
4542 }
@@ -49,27 +46,24 @@ func (t *headingIDTransformer) processHeading(heading *ast.Heading, source []byt
4946 return
5047 }
5148
52- id := t . generateUniqueID (headingText )
49+ id := generateUniqueID (headingText , usedIDs )
5350 heading .SetAttributeString ("id" , []byte (id ))
5451}
5552
5653// generateUniqueID 生成唯一的标题 ID,遇到重复自动添加后缀
57- func ( t * headingIDTransformer ) generateUniqueID (text string ) string {
54+ func generateUniqueID (text string , usedIDs map [ string ] int ) string {
5855 baseID := generateHeadingID (text )
5956 if baseID == "" {
6057 baseID = "heading"
6158 }
6259
63- t .mu .Lock ()
64- defer t .mu .Unlock ()
65-
66- count , exists := t .usedIDs [baseID ]
60+ count , exists := usedIDs [baseID ]
6761 if ! exists {
68- t . usedIDs [baseID ] = 1
62+ usedIDs [baseID ] = 1
6963 return baseID
7064 }
7165
72- t . usedIDs [baseID ] = count + 1
66+ usedIDs [baseID ] = count + 1
7367 return fmt .Sprintf ("%s-%d" , baseID , count + 1 )
7468}
7569
0 commit comments