@@ -23,8 +23,10 @@ import (
2323 "slices"
2424 "sort"
2525 "strings"
26+ "sync"
2627 "unicode"
2728
29+ "golang.org/x/sync/errgroup"
2830 sitter "github.com/smacker/go-tree-sitter"
2931
3032 "github.com/cloudwego/abcoder/lang/cpp"
@@ -187,6 +189,8 @@ func (c *Collector) Collect(ctx context.Context) error {
187189 if err != nil {
188190 return err
189191 }
192+ } else if c .Language == uniast .Cpp {
193+ root_syms = c .ScannerFileForConCurrentCPPScan (ctx )
190194 } else {
191195 root_syms = c .ScannerFile (ctx )
192196 }
@@ -1128,6 +1132,129 @@ func (c *Collector) ScannerFile(ctx context.Context) []*DocumentSymbol {
11281132 return root_syms
11291133}
11301134
1135+ func (c * Collector ) ScannerFileForConCurrentCPPScan (ctx context.Context ) []* DocumentSymbol {
1136+ c .configureLSP (ctx )
1137+ excludes := make ([]string , len (c .Excludes ))
1138+ for i , e := range c .Excludes {
1139+ if ! filepath .IsAbs (e ) {
1140+ excludes [i ] = filepath .Join (c .repo , e )
1141+ } else {
1142+ excludes [i ] = e
1143+ }
1144+ }
1145+
1146+ var paths []string
1147+ scanner := func (path string , info os.FileInfo , err error ) error {
1148+ if err != nil {
1149+ return err
1150+ }
1151+ if info .IsDir () {
1152+ return nil
1153+ }
1154+ for _ , e := range excludes {
1155+ if strings .HasPrefix (path , e ) {
1156+ return nil
1157+ }
1158+ }
1159+
1160+ if c .spec .ShouldSkip (path ) {
1161+ return nil
1162+ }
1163+
1164+ paths = append (paths , path )
1165+ return nil
1166+ }
1167+
1168+ if err := filepath .Walk (c .repo , scanner ); err != nil {
1169+ log .Error ("scan files failed: %v" , err )
1170+ }
1171+
1172+ // pre-open all files sequentially to avoid concurrent map writes in cli.files
1173+ for _ , path := range paths {
1174+ _ , err := c .cli .DidOpen (ctx , NewURI (path ))
1175+ if err != nil {
1176+ log .Error ("open file failed: %v" , err )
1177+ }
1178+ }
1179+
1180+ var root_syms []* DocumentSymbol
1181+ var mu sync.Mutex
1182+
1183+ var eg errgroup.Group
1184+ // Limit concurrency to not overwhelm the LSP server
1185+ eg .SetLimit (32 )
1186+
1187+ for _ , path := range paths {
1188+ path := path // capture loop variable
1189+ eg .Go (func () error {
1190+ mu .Lock ()
1191+ file := c .files [path ]
1192+ if file == nil {
1193+ rel , err := filepath .Rel (c .repo , path )
1194+ if err == nil {
1195+ file = uniast .NewFile (rel )
1196+ c .files [path ] = file
1197+ }
1198+ }
1199+ mu .Unlock ()
1200+
1201+ if file == nil {
1202+ return nil
1203+ }
1204+
1205+ // 解析use语句
1206+ content , err := os .ReadFile (path )
1207+ if err != nil {
1208+ return nil
1209+ }
1210+ uses , err := c .spec .FileImports (content )
1211+ if err != nil {
1212+ log .Error ("parse file %s use statements failed: %v" , path , err )
1213+ } else {
1214+ mu .Lock ()
1215+ file .Imports = uses
1216+ mu .Unlock ()
1217+ }
1218+
1219+ // collect symbols
1220+ uri := NewURI (path )
1221+ symbols , err := c .cli .DocumentSymbols (ctx , uri )
1222+ if err != nil {
1223+ return nil
1224+ }
1225+
1226+ var local_syms []* DocumentSymbol
1227+ for _ , sym := range symbols {
1228+ // collect content
1229+ symContent , err := c .cli .Locate (sym .Location )
1230+ if err != nil {
1231+ continue
1232+ }
1233+ // collect tokens
1234+ tokens , err := c .cli .SemanticTokens (ctx , sym .Location )
1235+ if err != nil {
1236+ continue
1237+ }
1238+ sym .Text = symContent
1239+ sym .Tokens = tokens
1240+ local_syms = append (local_syms , sym )
1241+ }
1242+
1243+ mu .Lock ()
1244+ for _ , sym := range local_syms {
1245+ c .addSymbol (sym .Location , sym )
1246+ root_syms = append (root_syms , sym )
1247+ }
1248+ mu .Unlock ()
1249+
1250+ return nil
1251+ })
1252+ }
1253+
1254+ _ = eg .Wait ()
1255+ return root_syms
1256+ }
1257+
11311258func (c * Collector ) ScannerByTreeSitter (ctx context.Context ) ([]* DocumentSymbol , error ) {
11321259 var modulePaths []string
11331260 // Java uses parsing pom method to obtain hierarchical relationships
0 commit comments