1+ // @index Graph building service that orchestrates parsing, persistence, and search indexing.
12package service
23
34import (
@@ -265,10 +266,8 @@ func (e *UnreadableFilesError) Error() string {
265266}
266267
267268// Build walks source files, stores parsed graph data, and rebuilds search docs.
268- // @intent 지원 언어 소스를 그래프와 검색 문서로 일괄 동기화한다 .
269+ // @intent perform a full graph build from the specified directory .
269270// @sideEffect 파일 시스템을 읽고 그래프 저장소·DB·검색 인덱스를 갱신한다.
270- // @requires s.Store, s.Walkers가 초기화되어 있어야 한다.
271- // @mutates 그래프 노드/엣지/어노테이션, search_documents
272271func (s * GraphService ) Build (ctx context.Context , opts BuildOptions ) (BuildStats , error ) {
273272 var stats BuildStats
274273
@@ -628,6 +627,8 @@ func partitionBuildEdges(edgeBatches []parsedBuildEdgeBatch) ([]model.Edge, []pa
628627 return implementsEdges , otherBatches
629628}
630629
630+ // splitImplementsEdges separates implements edges from other relationship types.
631+ // @intent ensure interface fulfillment edges are handled before call dispatch resolution.
631632func splitImplementsEdges (edges []model.Edge ) ([]model.Edge , []model.Edge ) {
632633 var implementsEdges []model.Edge
633634 var otherEdges []model.Edge
@@ -641,6 +642,8 @@ func splitImplementsEdges(edges []model.Edge) ([]model.Edge, []model.Edge) {
641642 return implementsEdges , otherEdges
642643}
643644
645+ // importEdgesByFile groups import-from edges by their source file path.
646+ // @intent optimize edge resolution by pre-loading import context for each file.
644647func importEdgesByFile (edgeBatches []parsedBuildEdgeBatch ) map [string ][]model.Edge {
645648 byFile := make (map [string ][]model.Edge , len (edgeBatches ))
646649 for _ , batch := range edgeBatches {
@@ -654,6 +657,8 @@ func importEdgesByFile(edgeBatches []parsedBuildEdgeBatch) map[string][]model.Ed
654657 return byFile
655658}
656659
660+ // chunkWithImportWarmup combines a chunk of call edges with their file's import edges.
661+ // @intent ensure the edge resolver has enough context to resolve call targets through imports.
657662func chunkWithImportWarmup (chunk []model.Edge , imports []model.Edge ) []model.Edge {
658663 if len (chunk ) == 0 {
659664 return nil
@@ -1313,6 +1318,8 @@ func parseForBuild(ctx context.Context, parser Parser, relPath string, content [
13131318 return nodes , edges , nil , "" , err
13141319}
13151320
1321+ // collectLanguagePackages discovers language-specific package information within the build directory.
1322+ // @intent identify package boundaries and file memberships to populate the graph's package structure.
13161323func (s * GraphService ) collectLanguagePackages (ctx context.Context , absDir string , opts BuildOptions ) map [string ]languagePackageInfo {
13171324 merged := make (map [string ]languagePackageInfo )
13181325 ambiguous := make (map [string ]struct {})
@@ -1340,10 +1347,14 @@ func (s *GraphService) collectLanguagePackages(ctx context.Context, absDir strin
13401347 return merged
13411348}
13421349
1350+ // packageDiscoverySpecs collects language specifications for all active parsers and walkers.
1351+ // @intent provide a unique set of discovery rules for all supported source languages.
13431352func (s * GraphService ) packageDiscoverySpecs () []* treesitter.LangSpec {
13441353 if len (s .Walkers ) == 0 && len (s .Parsers ) == 0 {
13451354 return nil
13461355 }
1356+ // parserWithSpec exposes language metadata from parsers that own a LangSpec.
1357+ // @intent let package discovery collect language specs without depending on a concrete parser type.
13471358 type parserWithSpec interface {
13481359 Spec () * treesitter.LangSpec
13491360 }
@@ -1382,10 +1393,14 @@ func (s *GraphService) packageDiscoverySpecs() []*treesitter.LangSpec {
13821393 return specs
13831394}
13841395
1396+ // withImportPackageContext attaches discovered package names to the context for use during edge resolution.
1397+ // @intent ensure cross-package imports can be resolved using their semantic names.
13851398func (s * GraphService ) withImportPackageContext (ctx context.Context , packages map [string ]languagePackageInfo ) context.Context {
13861399 return treesitter .WithImportPackages (ctx , importPackageNames (packages ))
13871400}
13881401
1402+ // mergeLanguagePackages aggregates discovered packages into a central map while filtering ambiguous paths.
1403+ // @intent consolidate package discovery results while discarding conflicting definitions.
13891404func mergeLanguagePackages (dst map [string ]languagePackageInfo , ambiguous map [string ]struct {}, src map [string ]languagePackageInfo ) {
13901405 for importPath , pkg := range src {
13911406 if importPath == "" {
@@ -1410,6 +1425,8 @@ func mergeLanguagePackages(dst map[string]languagePackageInfo, ambiguous map[str
14101425 }
14111426}
14121427
1428+ // importPackageNames extracts a mapping of import paths to package names.
1429+ // @intent allow the edge resolver to look up package names by their source import paths.
14131430func importPackageNames (packages map [string ]languagePackageInfo ) map [string ]string {
14141431 if len (packages ) == 0 {
14151432 return nil
@@ -1423,6 +1440,8 @@ func importPackageNames(packages map[string]languagePackageInfo) map[string]stri
14231440 return names
14241441}
14251442
1443+ // packageNodes converts discovered package info into graph nodes.
1444+ // @intent project package metadata into the graph schema for persistence.
14261445func packageNodes (packages map [string ]languagePackageInfo ) []model.Node {
14271446 if len (packages ) == 0 {
14281447 return nil
@@ -1444,6 +1463,8 @@ func packageNodes(packages map[string]languagePackageInfo) []model.Node {
14441463 return nodes
14451464}
14461465
1466+ // packageContainsEdgeCount returns the total number of file-to-package containment relationships.
1467+ // @intent estimate the edge overhead for package structural nodes.
14471468func packageContainsEdgeCount (packages map [string ]languagePackageInfo ) int {
14481469 count := 0
14491470 for _ , pkg := range packages {
@@ -1452,6 +1473,8 @@ func packageContainsEdgeCount(packages map[string]languagePackageInfo) int {
14521473 return count
14531474}
14541475
1476+ // upsertPackageNodes persists discovered package nodes into the graph store.
1477+ // @intent ensure package nodes exist before their member files are linked.
14551478func upsertPackageNodes (ctx context.Context , txStore store.GraphStore , packages map [string ]languagePackageInfo ) error {
14561479 nodes := packageNodes (packages )
14571480 if len (nodes ) == 0 {
@@ -1460,6 +1483,8 @@ func upsertPackageNodes(ctx context.Context, txStore store.GraphStore, packages
14601483 return txStore .UpsertNodes (ctx , nodes )
14611484}
14621485
1486+ // upsertPackageContainsEdges links package nodes to their member file nodes.
1487+ // @intent populate the graph's structural hierarchy by connecting packages to their source files.
14631488func upsertPackageContainsEdges (ctx context.Context , txStore store.GraphStore , packages map [string ]languagePackageInfo ) error {
14641489 if len (packages ) == 0 {
14651490 return nil
@@ -1498,6 +1523,8 @@ func upsertPackageContainsEdges(ctx context.Context, txStore store.GraphStore, p
14981523 return txStore .UpsertEdges (ctx , edges )
14991524}
15001525
1526+ // sortedPackageImportPaths returns a deterministic list of all discovered import paths.
1527+ // @intent keep package-related database operations stable across build runs.
15011528func sortedPackageImportPaths (packages map [string ]languagePackageInfo ) []string {
15021529 paths := make ([]string , 0 , len (packages ))
15031530 for importPath := range packages {
@@ -1507,6 +1534,8 @@ func sortedPackageImportPaths(packages map[string]languagePackageInfo) []string
15071534 return paths
15081535}
15091536
1537+ // packageFilePaths extracts all unique file paths belonging to the discovered packages.
1538+ // @intent collect all files that need to be linked to their containing package nodes.
15101539func packageFilePaths (packages map [string ]languagePackageInfo ) []string {
15111540 seen := make (map [string ]struct {})
15121541 var paths []string
@@ -1523,6 +1552,8 @@ func packageFilePaths(packages map[string]languagePackageInfo) []string {
15231552 return paths
15241553}
15251554
1555+ // singleNodeOfKind returns the first node in a list that matches the specified kind, or nil if none or multiple exist.
1556+ // @intent ensure unambiguous node selection during structural edge linking.
15261557func singleNodeOfKind (nodes []model.Node , kind model.NodeKind ) * model.Node {
15271558 var found * model.Node
15281559 for i := range nodes {
@@ -1537,11 +1568,15 @@ func singleNodeOfKind(nodes []model.Node, kind model.NodeKind) *model.Node {
15371568 return found
15381569}
15391570
1571+ // packageContainsFingerprint generates a stable identifier for a package-to-file relationship.
1572+ // @intent ensure package structural edges can be upserted without duplication.
15401573func packageContainsFingerprint (importPath , filePath string ) string {
15411574 sum := sha256 .Sum256 ([]byte (importPath + "\x00 " + filePath ))
15421575 return fmt .Sprintf ("contains:package:%x" , sum )
15431576}
15441577
1578+ // appendUniqueString appends a string to a slice only if it is not already present.
1579+ // @intent maintain a unique set of strings while preserving insertion order for small sets.
15451580func appendUniqueString (values []string , value string ) []string {
15461581 for _ , existing := range values {
15471582 if existing == value {
@@ -1551,6 +1586,8 @@ func appendUniqueString(values []string, value string) []string {
15511586 return append (values , value )
15521587}
15531588
1589+ // appendUniqueStrings appends multiple strings to a slice, ensuring each is unique.
1590+ // @intent aggregate strings from multiple sources while filtering duplicates.
15541591func appendUniqueStrings (values []string , add ... string ) []string {
15551592 for _ , value := range add {
15561593 values = appendUniqueString (values , value )
0 commit comments