codegraphcontext-rust 0.9.19__tar.gz → 0.9.20__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/PKG-INFO +1 -1
  2. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/pyproject.toml +1 -1
  3. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/resolution/calls.rs +70 -14
  4. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-python/src/lib.rs +21 -3
  5. codegraphcontext_rust-0.9.20/src/codegraphcontext/tools/indexing/angular_routes.py +396 -0
  6. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/cgc_wiki_cli.py +2 -2
  7. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/engine.py +7 -3
  8. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/persistence/duckdb_writer.py +76 -20
  9. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/route_extraction.py +7 -0
  10. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/LICENSE +0 -0
  11. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/README.md +0 -0
  12. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/Cargo.lock +0 -0
  13. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/Cargo.toml +0 -0
  14. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/Cargo.toml +0 -0
  15. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/c_lang.rs +0 -0
  16. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/cpp.rs +0 -0
  17. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/csharp.rs +0 -0
  18. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/dart.rs +0 -0
  19. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/elixir.rs +0 -0
  20. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/go.rs +0 -0
  21. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/haskell.rs +0 -0
  22. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/java.rs +0 -0
  23. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/javascript.rs +0 -0
  24. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/kotlin.rs +0 -0
  25. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/mod.rs +0 -0
  26. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/perl.rs +0 -0
  27. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/php.rs +0 -0
  28. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/python.rs +0 -0
  29. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/ruby.rs +0 -0
  30. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/rust_lang.rs +0 -0
  31. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/scala.rs +0 -0
  32. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/swift.rs +0 -0
  33. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/ts_common.rs +0 -0
  34. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/tsx.rs +0 -0
  35. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lang/typescript.rs +0 -0
  36. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/lib.rs +0 -0
  37. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/parser.rs +0 -0
  38. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/resolution/inheritance.rs +0 -0
  39. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/resolution/mod.rs +0 -0
  40. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/sanitize.rs +0 -0
  41. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-core/src/types.rs +0 -0
  42. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-python/Cargo.toml +0 -0
  43. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/cgc-python/src/conversions.rs +0 -0
  44. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/tree-sitter-perl-vendor/Cargo.toml +0 -0
  45. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/tree-sitter-perl-vendor/build.rs +0 -0
  46. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/tree-sitter-perl-vendor/src/grammar/alloc.h +0 -0
  47. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/tree-sitter-perl-vendor/src/grammar/array.h +0 -0
  48. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/tree-sitter-perl-vendor/src/grammar/parser.c +0 -0
  49. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/tree-sitter-perl-vendor/src/grammar/parser.h +0 -0
  50. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/tree-sitter-perl-vendor/src/grammar/scanner.c +0 -0
  51. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/tree-sitter-perl-vendor/src/grammar/tree_sitter/parser.h +0 -0
  52. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/rust/tree-sitter-perl-vendor/src/lib.rs +0 -0
  53. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/__init__.py +0 -0
  54. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/__main__.py +0 -0
  55. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/cli/__init__.py +0 -0
  56. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/cli/cli_helpers.py +0 -0
  57. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/cli/config_manager.py +0 -0
  58. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/cli/main.py +0 -0
  59. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/cli/registry_commands.py +0 -0
  60. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/cli/setup_macos.py +0 -0
  61. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/cli/setup_wizard.py +0 -0
  62. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/cli/visualizer.py +0 -0
  63. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/__init__.py +0 -0
  64. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/bundle_registry.py +0 -0
  65. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/cgc_bundle.py +0 -0
  66. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/cgcignore.py +0 -0
  67. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/database.py +0 -0
  68. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/database_falkordb.py +0 -0
  69. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/database_falkordb_remote.py +0 -0
  70. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/database_kuzu.py +0 -0
  71. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/falkor_worker.py +0 -0
  72. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/jobs.py +0 -0
  73. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/core/watcher.py +0 -0
  74. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/prompts.py +0 -0
  75. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/server.py +0 -0
  76. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tool_definitions.py +0 -0
  77. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/__init__.py +0 -0
  78. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
  79. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/code_finder.py +0 -0
  80. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/graph_builder.py +0 -0
  81. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/handlers/analysis_handlers.py +0 -0
  82. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/handlers/indexing_handlers.py +0 -0
  83. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/handlers/management_handlers.py +0 -0
  84. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/handlers/query_handlers.py +0 -0
  85. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/handlers/watcher_handlers.py +0 -0
  86. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/__init__.py +0 -0
  87. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/constants.py +0 -0
  88. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/context_generator.py +0 -0
  89. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/discovery.py +0 -0
  90. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/execution_flows.py +0 -0
  91. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/module_grouping.py +0 -0
  92. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/noise_filter.py +0 -0
  93. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/op_param_extraction.py +0 -0
  94. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/persistence/__init__.py +0 -0
  95. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/persistence/parquet_loader.py +0 -0
  96. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/persistence/writer.py +0 -0
  97. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/pipeline.py +0 -0
  98. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/pre_scan.py +0 -0
  99. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/rationale_extraction.py +0 -0
  100. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/resolution/__init__.py +0 -0
  101. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/resolution/calls.py +0 -0
  102. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/resolution/inheritance.py +0 -0
  103. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/sanitize.py +0 -0
  104. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/schema.py +0 -0
  105. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/schema_contract.py +0 -0
  106. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/indexing/scip_pipeline.py +0 -0
  107. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/c.py +0 -0
  108. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/cpp.py +0 -0
  109. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/csharp.py +0 -0
  110. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/dart.py +0 -0
  111. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/elixir.py +0 -0
  112. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/go.py +0 -0
  113. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/haskell.py +0 -0
  114. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/java.py +0 -0
  115. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/javascript.py +0 -0
  116. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
  117. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/perl.py +0 -0
  118. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/php.py +0 -0
  119. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/python.py +0 -0
  120. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/ruby.py +0 -0
  121. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/rust.py +0 -0
  122. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/scala.py +0 -0
  123. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/swift.py +0 -0
  124. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/typescript.py +0 -0
  125. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/languages/typescriptjsx.py +0 -0
  126. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/package_resolver.py +0 -0
  127. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
  128. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
  129. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
  130. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/dart_toolkit.py +0 -0
  131. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
  132. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +0 -0
  133. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
  134. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
  135. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/perl_toolkit.py +0 -0
  136. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
  137. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
  138. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
  139. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
  140. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
  141. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
  142. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/scip_indexer.py +0 -0
  143. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/scip_pb2.py +0 -0
  144. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/system.py +0 -0
  145. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/tools/tree_sitter_parser.py +0 -0
  146. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/utils/debug_log.py +0 -0
  147. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/utils/path_ignore.py +0 -0
  148. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/utils/repo_path.py +0 -0
  149. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/utils/tree_sitter_manager.py +0 -0
  150. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/utils/visualize_graph.py +0 -0
  151. {codegraphcontext_rust-0.9.19 → codegraphcontext_rust-0.9.20}/src/codegraphcontext/viz/server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraphcontext-rust
3
- Version: 0.9.19
3
+ Version: 0.9.20
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.10
6
6
  Classifier: Programming Language :: Python :: 3.11
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "codegraphcontext-rust"
7
- version = "0.9.19"
7
+ version = "0.9.20"
8
8
  description = "Rust-accelerated code indexing engine for CodeGraphContext. 19 languages, 5-18x faster parsing via tree-sitter + rayon."
9
9
  authors = [
10
10
  { name = "Shashank Shekhar Singh", email = "shashankshekharsingh1205@gmail.com" },
@@ -351,6 +351,22 @@ pub struct CallGroups {
351
351
  pub cls_to_cls: Vec<ResolvedCall>,
352
352
  pub file_to_fn: Vec<ResolvedCall>,
353
353
  pub file_to_cls: Vec<ResolvedCall>,
354
+ /// Calls that produced no edge, with the reason (Python builtins excluded).
355
+ pub unresolved: Vec<UnresolvedCall>,
356
+ }
357
+
358
+ /// A call the resolver dropped, kept for diagnostics (upstream: `record_skip`).
359
+ #[derive(Debug, Clone)]
360
+ pub struct UnresolvedCall {
361
+ pub caller_file_path: String,
362
+ pub caller_name: Option<String>,
363
+ pub line_number: usize,
364
+ pub called_name: String,
365
+ pub full_call_name: String,
366
+ /// Receiver type the extractor inferred, if any
367
+ pub receiver_type: Option<String>,
368
+ /// external_receiver_type | external_chain_type | receiver_namesake | skip_external
369
+ pub reason: &'static str,
354
370
  }
355
371
 
356
372
  /// Locate the file declaring type `ty`. Returns (file, exact): exact when
@@ -375,8 +391,9 @@ fn locate_type(
375
391
  Some((paths[0].clone(), false))
376
392
  }
377
393
 
378
- /// Resolve a single function call to its target.
379
- pub fn resolve_function_call(
394
+ /// Resolve a single function call to its target; `Err(reason)` when the
395
+ /// call produces no edge.
396
+ pub fn resolve_call(
380
397
  call: &CallInput,
381
398
  caller_file_path: &str,
382
399
  local_names: &HashSet<String>,
@@ -384,7 +401,7 @@ pub fn resolve_function_call(
384
401
  imports_map: &HashMap<String, Vec<String>>,
385
402
  types: &TypeIndex,
386
403
  skip_external: bool,
387
- ) -> Option<ResolvedCall> {
404
+ ) -> Result<ResolvedCall, &'static str> {
388
405
  let called_name = &call.name;
389
406
  let full_call = &call.full_name;
390
407
 
@@ -393,7 +410,7 @@ pub fn resolve_function_call(
393
410
  // from another language — is a regular method call.
394
411
  let is_python = caller_file_path.ends_with(".py") || caller_file_path.ends_with(".pyi");
395
412
  if is_python && !full_call.contains('.') && PYTHON_BUILTINS.contains(&called_name.as_str()) {
396
- return None;
413
+ return Err("python_builtin");
397
414
  }
398
415
 
399
416
  let base_obj = if full_call.contains('.') {
@@ -514,7 +531,7 @@ pub fn resolve_function_call(
514
531
  resolved = Some((found.map(|(_, f, _)| f).unwrap_or(file), 4));
515
532
  }
516
533
  // e.g. repo.findAll().stream(): target is outside the graph
517
- ChainType::External => return None,
534
+ ChainType::External => return Err("external_chain_type"),
518
535
  ChainType::Unknown => {}
519
536
  }
520
537
  }
@@ -581,7 +598,7 @@ pub fn resolve_function_call(
581
598
  // The receiver's type is known but lives outside the repo: the
582
599
  // target can't be in the graph (upstream: receiver_resolution_failed).
583
600
  if external_receiver_type {
584
- return None;
601
+ return Err("external_receiver_type");
585
602
  }
586
603
  // Pointing it at the caller's file would bind it to a same-named
587
604
  // local function (false recursion), so drop it in that case —
@@ -589,7 +606,7 @@ pub fn resolve_function_call(
589
606
  if local_names.contains(called_name.as_str())
590
607
  || call.context_name.as_deref() == Some(called_name.as_str())
591
608
  {
592
- return None;
609
+ return Err("receiver_namesake");
593
610
  }
594
611
  resolved = Some((own(), 9));
595
612
  } else if !qualified_other && local_names.contains(called_name.as_str()) {
@@ -606,7 +623,7 @@ pub fn resolve_function_call(
606
623
  })
607
624
  .collect();
608
625
  if candidates.is_empty() && qualified_other && local_names.contains(called_name.as_str()) {
609
- return None;
626
+ return Err("receiver_namesake");
610
627
  }
611
628
  // Try matching via local imports
612
629
  for p in &candidates {
@@ -628,7 +645,7 @@ pub fn resolve_function_call(
628
645
  }
629
646
 
630
647
  if skip_external && is_unresolved_external {
631
- return None;
648
+ return Err("skip_external");
632
649
  }
633
650
 
634
651
  let (resolved_path, tier) = resolved.unwrap_or_else(|| (own(), 9));
@@ -638,7 +655,7 @@ pub fn resolve_function_call(
638
655
  if let (Some(name), Some(_), Some(line)) =
639
656
  (&call.context_name, &call.context_type, call.context_line)
640
657
  {
641
- Some(ResolvedCall {
658
+ Ok(ResolvedCall {
642
659
  call_type: "function".to_string(),
643
660
  caller_name: Some(name.clone()),
644
661
  caller_file_path: caller_file_path.to_string(),
@@ -654,7 +671,7 @@ pub fn resolve_function_call(
654
671
  dispatch_class: dispatch_class.clone(),
655
672
  })
656
673
  } else {
657
- Some(ResolvedCall {
674
+ Ok(ResolvedCall {
658
675
  call_type: "file".to_string(),
659
676
  caller_name: None,
660
677
  caller_file_path: caller_file_path.to_string(),
@@ -672,6 +689,19 @@ pub fn resolve_function_call(
672
689
  }
673
690
  }
674
691
 
692
+ /// Resolve a single function call to its target (None when dropped).
693
+ pub fn resolve_function_call(
694
+ call: &CallInput,
695
+ caller_file_path: &str,
696
+ local_names: &HashSet<String>,
697
+ local_imports: &HashMap<String, String>,
698
+ imports_map: &HashMap<String, Vec<String>>,
699
+ types: &TypeIndex,
700
+ skip_external: bool,
701
+ ) -> Option<ResolvedCall> {
702
+ resolve_call(call, caller_file_path, local_names, local_imports, imports_map, types, skip_external).ok()
703
+ }
704
+
675
705
  /// Language extension map for filtering imports_map by language.
676
706
  fn lang_extensions(lang: &str) -> Option<&[&str]> {
677
707
  match lang {
@@ -763,7 +793,7 @@ pub fn build_function_call_groups(
763
793
  };
764
794
 
765
795
  for call in &file_data.calls {
766
- let first = match resolve_function_call(
796
+ let first = match resolve_call(
767
797
  call,
768
798
  caller_file_path,
769
799
  &local_names_owned,
@@ -772,8 +802,20 @@ pub fn build_function_call_groups(
772
802
  &types,
773
803
  skip_external,
774
804
  ) {
775
- Some(r) => r,
776
- None => continue,
805
+ Ok(r) => r,
806
+ Err("python_builtin") => continue,
807
+ Err(reason) => {
808
+ groups.unresolved.push(UnresolvedCall {
809
+ caller_file_path: caller_file_path.to_string(),
810
+ caller_name: call.context_name.clone(),
811
+ line_number: call.line_number,
812
+ called_name: call.name.clone(),
813
+ full_call_name: call.full_name.clone(),
814
+ receiver_type: call.inferred_obj_type.clone(),
815
+ reason,
816
+ });
817
+ continue;
818
+ }
777
819
  };
778
820
 
779
821
  // Virtual dispatch (upstream: Java interface fan-out): a call
@@ -1313,4 +1355,18 @@ mod tests {
1313
1355
  let r = resolve_function_call(&call, "/r/Log.ts", &HashSet::new(), &HashMap::new(), &HashMap::new(), &TypeIndex::default(), false);
1314
1356
  assert!(r.is_none());
1315
1357
  }
1358
+
1359
+ #[test]
1360
+ fn test_unresolved_calls_are_recorded_with_reason() {
1361
+ let mut f = file("/r/A.java", &[("A", &["run", "add"], &[])], vec![]);
1362
+ let mut ext = receiver_call("add", "items.add", Some("List"));
1363
+ ext.context_name = Some("run".into());
1364
+ let mut builtin = receiver_call("len", "len", None);
1365
+ builtin.context_name = Some("run".into());
1366
+ f.calls = vec![ext, builtin];
1367
+ let groups = build_function_call_groups(&[f], &HashMap::new(), &HashMap::new(), false);
1368
+ assert_eq!(groups.unresolved.len(), 1); // the builtin isn't recorded (and A.java isn't Python anyway)
1369
+ let u = &groups.unresolved[0];
1370
+ assert_eq!((u.called_name.as_str(), u.reason, u.receiver_type.as_deref()), ("add", "external_receiver_type", Some("List")));
1371
+ }
1316
1372
  }
@@ -88,12 +88,13 @@ fn parse_and_prescan(
88
88
 
89
89
  /// Convert Python all_file_data + imports_map into Rust types, run resolution, return results.
90
90
  #[pyfunction]
91
- #[pyo3(signature = (all_file_data, imports_map, skip_external=false))]
91
+ #[pyo3(signature = (all_file_data, imports_map, skip_external=false, with_unresolved=false))]
92
92
  fn resolve_call_groups(
93
93
  py: Python<'_>,
94
94
  all_file_data: &Bound<'_, PyList>,
95
95
  imports_map: &Bound<'_, PyDict>,
96
96
  skip_external: bool,
97
+ with_unresolved: bool,
97
98
  ) -> PyResult<PyObject> {
98
99
  // Convert imports_map: Python dict -> Rust HashMap
99
100
  let rust_imports_map = py_dict_to_imports_map(imports_map)?;
@@ -120,8 +121,25 @@ fn resolve_call_groups(
120
121
  build_function_call_groups(&rust_files, &rust_imports_map, &file_class_lookup, skip_external)
121
122
  });
122
123
 
123
- // Convert back to Python: 6-tuple of lists of dicts
124
- call_groups_to_py(py, &groups)
124
+ // Convert back to Python: 6-tuple of lists of dicts; with_unresolved
125
+ // returns (6-tuple, [unresolved call dicts]) instead.
126
+ let call_groups = call_groups_to_py(py, &groups)?;
127
+ if !with_unresolved {
128
+ return Ok(call_groups);
129
+ }
130
+ let unresolved = PyList::empty(py);
131
+ for u in &groups.unresolved {
132
+ let d = PyDict::new(py);
133
+ d.set_item("caller_file_path", &u.caller_file_path)?;
134
+ d.set_item("caller_name", &u.caller_name)?;
135
+ d.set_item("line_number", u.line_number)?;
136
+ d.set_item("called_name", &u.called_name)?;
137
+ d.set_item("full_call_name", &u.full_call_name)?;
138
+ d.set_item("receiver_type", &u.receiver_type)?;
139
+ d.set_item("reason", u.reason)?;
140
+ unresolved.append(d)?;
141
+ }
142
+ Ok(PyTuple::new(py, [call_groups, unresolved.into_any().unbind()])?.into_any().unbind())
125
143
  }
126
144
 
127
145
  /// Resolve inheritance links.
@@ -0,0 +1,396 @@
1
+ """Angular Router: route tables, lazy-loaded modules and navigation calls.
2
+
3
+ Routes come from `Routes` arrays passed to `RouterModule.forRoot/forChild`
4
+ or `provideRouter`, or exported for `loadChildren`. Full paths are built by
5
+ following `loadChildren: () => import('./x/x.module').then(m => m.XModule)`
6
+ from the root routes into the child module's routing file, so a page gets
7
+ `/home/tabs/tab1` rather than just `tab1`.
8
+
9
+ Navigations are `router.navigate([...])` / `router.navigateByUrl('...')`
10
+ calls, attributed to their enclosing function and matched to a route.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import os
16
+ import re
17
+ from typing import Any, Dict, List, Optional, Tuple
18
+
19
+ _ROUTE_ARRAY_DECL = re.compile(
20
+ r"\b(?:export\s+)?(?:const|let|var)\s+(\w+)\s*(?::\s*(Routes|Route\s*\[\s*\]))?\s*=\s*\["
21
+ )
22
+ _ROUTER_CALL = re.compile(r"\b(?:RouterModule\.(forRoot|forChild)|(provideRouter))\s*\(")
23
+ _IMPORT_SPEC = re.compile(r"import\(\s*['\"]([^'\"]+)['\"]\s*\)")
24
+ _THEN_SYMBOL = re.compile(r"\.then\(\s*\(?\s*(\w+)\s*\)?\s*=>\s*\1\.(\w+)")
25
+ _NAVIGATE = re.compile(r"\b(?:\w+\.)*\w*[Rr]outer\s*\.\s*(navigate|navigateByUrl)\s*\(")
26
+ _TS_EXTS = (".ts", ".tsx", ".js", ".mjs")
27
+
28
+
29
+ # ── Small JS literal parser ──────────────────────────────────────────
30
+
31
+ def _strip_comments(src: str) -> str:
32
+ """Blank out // and /* */ comments (string-aware, keeps offsets)."""
33
+ out, i, n = [], 0, len(src)
34
+ while i < n:
35
+ c = src[i]
36
+ if c in "'\"`":
37
+ j = i + 1
38
+ while j < n and src[j] != c:
39
+ j += 2 if src[j] == "\\" else 1
40
+ out.append(src[i:j + 1]); i = j + 1
41
+ elif src.startswith("//", i):
42
+ j = src.find("\n", i); j = n if j == -1 else j
43
+ out.append(" " * (j - i)); i = j
44
+ elif src.startswith("/*", i):
45
+ j = src.find("*/", i + 2); j = n if j == -1 else j + 2
46
+ out.append(re.sub(r"[^\n]", " ", src[i:j])); i = j
47
+ else:
48
+ out.append(c); i += 1
49
+ return "".join(out)
50
+
51
+
52
+ def _match_close(code: str, i: int) -> int:
53
+ """Index just past the bracket matching code[i] ('[', '{' or '(')."""
54
+ depth, n = 0, len(code)
55
+ while i < n:
56
+ c = code[i]
57
+ if c in "'\"`":
58
+ j = i + 1
59
+ while j < n and code[j] != c:
60
+ j += 2 if code[j] == "\\" else 1
61
+ i = j + 1
62
+ continue
63
+ if c in "[{(":
64
+ depth += 1
65
+ elif c in "]})":
66
+ depth -= 1
67
+ if depth == 0:
68
+ return i + 1
69
+ i += 1
70
+ return n
71
+
72
+
73
+ def _split_top(code: str, start: int, end: int) -> List[Tuple[int, int]]:
74
+ """Comma-separated top-level segments of code[start:end]."""
75
+ segs, depth, seg_start, i = [], 0, start, start
76
+ while i < end:
77
+ c = code[i]
78
+ if c in "'\"`":
79
+ j = i + 1
80
+ while j < end and code[j] != c:
81
+ j += 2 if code[j] == "\\" else 1
82
+ i = j + 1
83
+ continue
84
+ if c in "[{(":
85
+ depth += 1
86
+ elif c in "]})":
87
+ depth -= 1
88
+ elif c == "," and depth == 0:
89
+ segs.append((seg_start, i)); seg_start = i + 1
90
+ i += 1
91
+ segs.append((seg_start, end))
92
+ return [(a, b) for a, b in segs if code[a:b].strip()]
93
+
94
+
95
+ def _string_value(text: str) -> Optional[str]:
96
+ m = re.fullmatch(r"\s*(['\"`])(.*?)\1\s*", text, re.S)
97
+ return m.group(2) if m else None
98
+
99
+
100
+ def _parse_object(code: str, start: int, end: int) -> Dict[str, Tuple[str, int]]:
101
+ """Top-level `key: value` pairs of an object literal code[start:end] ({...})."""
102
+ props = {}
103
+ for a, b in _split_top(code, start + 1, end - 1):
104
+ m = re.match(r"\s*['\"]?(\w+)['\"]?\s*:", code[a:b])
105
+ if m:
106
+ vstart = a + m.end()
107
+ props[m.group(1)] = (code[vstart:b].strip(), vstart + (len(code[vstart:b]) - len(code[vstart:b].lstrip())))
108
+ return props
109
+
110
+
111
+ def _parse_routes(code: str, start: int, end: int) -> List[Dict[str, Any]]:
112
+ """Parse a routes array code[start:end] ([...]) into route dicts."""
113
+ routes = []
114
+ for a, b in _split_top(code, start + 1, end - 1):
115
+ seg = code[a:b].strip()
116
+ off = a + (len(code[a:b]) - len(code[a:b].lstrip()))
117
+ if seg.startswith("..."): # spread of another routes array
118
+ routes.append({"ref": seg[3:].strip(), "pos": off})
119
+ continue
120
+ if re.fullmatch(r"\w+", seg):
121
+ routes.append({"ref": seg, "pos": off})
122
+ continue
123
+ if not seg.startswith("{"):
124
+ continue
125
+ props = _parse_object(code, off, _match_close(code, off))
126
+ route: Dict[str, Any] = {"pos": off, "path": None}
127
+ if "path" in props:
128
+ route["path"] = _string_value(props["path"][0])
129
+ if "component" in props:
130
+ route["component"] = props["component"][0].strip()
131
+ if "redirectTo" in props:
132
+ route["redirect"] = _string_value(props["redirectTo"][0]) or props["redirectTo"][0]
133
+ for key in ("loadChildren", "loadComponent"):
134
+ if key in props:
135
+ val = props[key][0]
136
+ spec = _IMPORT_SPEC.search(val)
137
+ sym = _THEN_SYMBOL.search(val)
138
+ legacy = _string_value(val) # 'path/to/x.module#XModule'
139
+ if spec:
140
+ route[key] = (spec.group(1), sym.group(2) if sym else None)
141
+ elif legacy and "#" in legacy:
142
+ p, s = legacy.split("#", 1)
143
+ route[key] = (p, s)
144
+ if "children" in props and props["children"][0].startswith("["):
145
+ cstart = props["children"][1]
146
+ route["children"] = _parse_routes(code, cstart, _match_close(code, cstart))
147
+ routes.append(route)
148
+ return routes
149
+
150
+
151
+ # ── Per-file route tables ────────────────────────────────────────────
152
+
153
+ def _line_of(code: str, pos: int) -> int:
154
+ return code.count("\n", 0, pos) + 1
155
+
156
+
157
+ def _file_routes(code: str) -> Dict[str, Any]:
158
+ """Named route arrays and router registrations found in one file."""
159
+ arrays: Dict[str, Dict[str, Any]] = {}
160
+ for m in _ROUTE_ARRAY_DECL.finditer(code):
161
+ start = m.end() - 1
162
+ arrays[m.group(1)] = {
163
+ "routes": _parse_routes(code, start, _match_close(code, start)),
164
+ "typed": bool(m.group(2)),
165
+ }
166
+ registrations = [] # (mode, array name or None, inline routes or None)
167
+ for m in _ROUTER_CALL.finditer(code):
168
+ mode = "root" if (m.group(1) == "forRoot" or m.group(2)) else "child"
169
+ args_start = m.end()
170
+ while args_start < len(code) and code[args_start].isspace():
171
+ args_start += 1
172
+ if args_start < len(code) and code[args_start] == "[":
173
+ registrations.append((mode, None, _parse_routes(code, args_start, _match_close(code, args_start))))
174
+ else:
175
+ ident = re.match(r"(\w+)", code[args_start:])
176
+ if ident:
177
+ registrations.append((mode, ident.group(1), None))
178
+ return {"arrays": arrays, "registrations": registrations}
179
+
180
+
181
+ def _resolve_import(from_rel: str, spec: str, known: set) -> Optional[str]:
182
+ if not spec.startswith("."):
183
+ return None
184
+ base = os.path.normpath(os.path.join(os.path.dirname(from_rel), spec))
185
+ for cand in [base] + [base + e for e in _TS_EXTS] + [os.path.join(base, "index" + e) for e in _TS_EXTS]:
186
+ if cand in known:
187
+ return cand
188
+ return None
189
+
190
+
191
+ def _join(prefix: str, path: Optional[str]) -> str:
192
+ # `path: \`${PREFIX}/x\`` → dynamic segment
193
+ path = re.sub(r"\$\{[^}]*\}", ":param", path or "")
194
+ parts = [p for p in (prefix.strip("/"), path.strip("/")) if p]
195
+ return "/" + "/".join(parts)
196
+
197
+
198
+ def extract_angular_routes(
199
+ parsed_results: List[Dict[str, Any]],
200
+ repo_path: str,
201
+ ) -> List[Dict[str, Any]]:
202
+ """Angular routes with full paths, as route dicts (method PAGE/REDIRECT)."""
203
+ files: Dict[str, Dict[str, Any]] = {}
204
+ codes: Dict[str, str] = {}
205
+ for fd in parsed_results:
206
+ path = fd.get("path", "")
207
+ if fd.get("lang") not in ("typescript", "tsx", "javascript") or not os.path.exists(path):
208
+ continue
209
+ try:
210
+ src = open(path, encoding="utf-8", errors="replace").read()
211
+ except OSError:
212
+ continue
213
+ if "Route" not in src and "provideRouter" not in src:
214
+ continue
215
+ rel = os.path.relpath(path, repo_path)
216
+ code = _strip_comments(src)
217
+ info = _file_routes(code)
218
+ if info["arrays"] or info["registrations"]:
219
+ files[rel] = info
220
+ codes[rel] = code
221
+ if not files:
222
+ return []
223
+ known = {os.path.relpath(fd.get("path", ""), repo_path) for fd in parsed_results}
224
+
225
+ def array_by_name(rel: str, name: str):
226
+ arr = files.get(rel, {}).get("arrays", {}).get(name)
227
+ return arr["routes"] if arr else None
228
+
229
+ def child_tables(target: str, symbol: Optional[str]) -> List[Tuple[str, List]]:
230
+ """Route tables a lazy `loadChildren` target contributes."""
231
+ out = []
232
+ if target in files:
233
+ if symbol and array_by_name(target, symbol) is not None:
234
+ return [(target, array_by_name(target, symbol))]
235
+ out += [(target, routes) for _, routes in _registered(target)]
236
+ if not out: # the module imports a sibling *-routing.module
237
+ d = os.path.dirname(target)
238
+ for rel in files:
239
+ if os.path.dirname(rel) == d and rel != target and ("routing" in rel or "routes" in rel):
240
+ out += [(rel, routes) for mode, routes in _registered(rel) if mode == "child"]
241
+ return out
242
+
243
+ def _registered(rel: str) -> List[Tuple[str, List]]:
244
+ info = files[rel]
245
+ out = []
246
+ for mode, name, inline in info["registrations"]:
247
+ routes = inline if inline is not None else array_by_name(rel, name) if name else None
248
+ if routes is not None:
249
+ out.append((mode, routes))
250
+ return out
251
+
252
+ results: List[Dict[str, Any]] = []
253
+ emitted = set()
254
+ reached = set() # ids of route lists visited from a root
255
+
256
+ def walk(rel: str, routes: List, prefix: str, depth: int):
257
+ if depth > 12:
258
+ return
259
+ reached.add(id(routes))
260
+ for r in routes:
261
+ if "ref" in r: # ...OTHER_ROUTES / OTHER_ROUTES inside the array
262
+ ref = array_by_name(rel, r["ref"])
263
+ if ref is not None and id(ref) not in reached:
264
+ walk(rel, ref, prefix, depth + 1)
265
+ continue
266
+ full = _join(prefix, r.get("path"))
267
+ line = _line_of(codes[rel], r["pos"])
268
+ handler, method = None, "PAGE"
269
+ if r.get("component"):
270
+ handler = r["component"]
271
+ elif r.get("loadComponent"):
272
+ handler = r["loadComponent"][1] or r["loadComponent"][0]
273
+ elif r.get("loadChildren"):
274
+ handler = r["loadChildren"][1] or r["loadChildren"][0]
275
+ elif r.get("redirect") is not None:
276
+ to = str(r["redirect"])
277
+ handler = "→ " + (to if to.startswith("/") else _join(prefix, to))
278
+ method = "REDIRECT"
279
+ if handler and (method, full, rel, line) not in emitted:
280
+ emitted.add((method, full, rel, line))
281
+ results.append({"method": method, "path": full, "handler": handler,
282
+ "file": rel, "line": line, "framework": "angular"})
283
+ if r.get("children"):
284
+ walk(rel, r["children"], full, depth + 1)
285
+ if r.get("loadChildren"):
286
+ spec, symbol = r["loadChildren"]
287
+ target = _resolve_import(rel, spec, known)
288
+ if target:
289
+ for trel, troutes in child_tables(target, symbol):
290
+ if id(troutes) not in reached:
291
+ walk(trel, troutes, full, depth + 1)
292
+
293
+ for rel in files:
294
+ for mode, routes in _registered(rel):
295
+ if mode == "root":
296
+ walk(rel, routes, "", 0)
297
+ # Child tables never reached from a root (unknown mount point): keep
298
+ # their local paths so the pages are still listed.
299
+ for rel in files:
300
+ for mode, routes in _registered(rel):
301
+ if id(routes) not in reached:
302
+ walk(rel, routes, "", 0)
303
+ return results
304
+
305
+
306
+ # ── Navigations ──────────────────────────────────────────────────────
307
+
308
+ def _nav_target(code: str, args_start: int, kind: str) -> Optional[str]:
309
+ """Target path of a navigate([...]) / navigateByUrl('...') call.
310
+
311
+ Non-literal segments become `:param`; a path is absolute when its first
312
+ literal segment starts with '/'.
313
+ """
314
+ while args_start < len(code) and code[args_start].isspace():
315
+ args_start += 1
316
+ if kind == "navigate":
317
+ if args_start >= len(code) or code[args_start] != "[":
318
+ return None
319
+ end = _match_close(code, args_start)
320
+ parts, absolute = [], False
321
+ for idx, (a, b) in enumerate(_split_top(code, args_start + 1, end - 1)):
322
+ s = _string_value(code[a:b])
323
+ if s is None:
324
+ parts.append(":param")
325
+ continue
326
+ if idx == 0 and s.startswith("/"):
327
+ absolute = True
328
+ parts += [":param" if "${" in p else p for p in s.split("/") if p]
329
+ return ("/" if absolute else "") + "/".join(parts)
330
+ m = re.match(r"(['\"`])(.*?)\1", code[args_start:args_start + 500], re.S)
331
+ if not m:
332
+ return None
333
+ return re.sub(r"\$\{[^}]*\}", ":param", m.group(2).split("?")[0])
334
+
335
+
336
+ def _match_route(target: str, routes: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
337
+ """Best route for an absolute target path (literal segments must match)."""
338
+ if not target.startswith("/"):
339
+ return None
340
+ tseg = [s for s in target.strip("/").split("/") if s]
341
+ best, best_score = None, -1
342
+ for r in routes:
343
+ if r["method"] != "PAGE":
344
+ continue
345
+ rseg = [s for s in r["path"].strip("/").split("/") if s]
346
+ if len(rseg) != len(tseg):
347
+ continue
348
+ score = 0
349
+ for a, b in zip(rseg, tseg):
350
+ if a.startswith(":") or b == ":param":
351
+ continue
352
+ if a != b:
353
+ break
354
+ score += 1
355
+ else:
356
+ if score > best_score:
357
+ best, best_score = r, score
358
+ return best
359
+
360
+
361
+ def extract_navigations(
362
+ parsed_results: List[Dict[str, Any]],
363
+ repo_path: str,
364
+ angular_routes: List[Dict[str, Any]],
365
+ ) -> List[Dict[str, Any]]:
366
+ """router.navigate / navigateByUrl calls with their enclosing function and route."""
367
+ out = []
368
+ for fd in parsed_results:
369
+ path = fd.get("path", "")
370
+ if fd.get("lang") not in ("typescript", "tsx", "javascript") or not os.path.exists(path):
371
+ continue
372
+ try:
373
+ src = open(path, encoding="utf-8", errors="replace").read()
374
+ except OSError:
375
+ continue
376
+ if "navigate" not in src:
377
+ continue
378
+ code = _strip_comments(src)
379
+ rel = os.path.relpath(path, repo_path)
380
+ funcs = [(f.get("line_number", 0), f.get("end_line", 0) or f.get("line_number", 0), f.get("name", ""))
381
+ for f in fd.get("functions", [])]
382
+ for m in _NAVIGATE.finditer(code):
383
+ target = _nav_target(code, m.end(), m.group(1))
384
+ if target is None:
385
+ continue
386
+ line = _line_of(code, m.start())
387
+ enclosing = [f for f in funcs if f[0] <= line <= f[1]]
388
+ caller = max(enclosing, key=lambda f: f[0])[2] if enclosing else ""
389
+ route = _match_route(target, angular_routes)
390
+ out.append({
391
+ "caller_name": caller, "caller_path": rel, "line": line, "target": target,
392
+ "route_path": route["path"] if route else "",
393
+ "route_handler": route["handler"] if route else "",
394
+ "route_file": route["file"] if route else "",
395
+ })
396
+ return out
@@ -75,7 +75,7 @@ def cmd_index(args):
75
75
  specs = [(f, l, False) for f, l in files]
76
76
  results, imports_map = parse_and_prescan(specs)
77
77
  valid = [r for r in results if "error" not in r]
78
- call_groups = resolve_call_groups(valid, imports_map, False)
78
+ call_groups, unresolved = resolve_call_groups(valid, imports_map, False, True)
79
79
  inheritance, _ = resolve_inheritance(valid, imports_map)
80
80
  t_parse = time.time() - t0
81
81
  print(f"\nParse + resolve: {t_parse:.1f}s")
@@ -86,7 +86,7 @@ def cmd_index(args):
86
86
  db_path = os.path.join(output_dir, "graph.duckdb")
87
87
  t0 = time.time()
88
88
  writer = DuckDBGraphWriter(db_path)
89
- counts = writer.write_all(valid, repo_path, call_groups, inheritance)
89
+ counts = writer.write_all(valid, repo_path, call_groups, inheritance, unresolved=unresolved)
90
90
  t_write = time.time() - t0
91
91
 
92
92
  # Read back from DB (no duplicate extraction)
@@ -81,11 +81,15 @@ def pre_scan_for_imports(file_specs):
81
81
  return _rust_pre_scan(file_specs)
82
82
 
83
83
 
84
- def resolve_call_groups(all_file_data, imports_map, skip_external=False):
85
- """Resolve function calls into 6-category groups using Rust."""
84
+ def resolve_call_groups(all_file_data, imports_map, skip_external=False, with_unresolved=False):
85
+ """Resolve function calls into 6-category groups using Rust.
86
+
87
+ With ``with_unresolved=True`` returns ``(groups, unresolved)`` where
88
+ ``unresolved`` lists the calls that produced no edge and why.
89
+ """
86
90
  if not RUST_AVAILABLE:
87
91
  raise RuntimeError("Rust engine required for resolve_call_groups")
88
- return _rust_resolve_calls(all_file_data, imports_map, skip_external)
92
+ return _rust_resolve_calls(all_file_data, imports_map, skip_external, with_unresolved)
89
93
 
90
94
 
91
95
  def resolve_inheritance(all_file_data, imports_map):