codenib 0.1.0__py3-none-any.whl
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.
- codenib/__init__.py +62 -0
- codenib/__main__.py +7 -0
- codenib/_lazy.py +36 -0
- codenib/_version.py +26 -0
- codenib/agent/__init__.py +159 -0
- codenib/agent/agent_types.py +40 -0
- codenib/agent/boundary.py +129 -0
- codenib/agent/compile.py +219 -0
- codenib/agent/extract_agent.py +101 -0
- codenib/agent/harness.py +326 -0
- codenib/agent/history.py +242 -0
- codenib/agent/lsp_graph.py +878 -0
- codenib/agent/lsp_provider.py +484 -0
- codenib/agent/rerank_agent.py +419 -0
- codenib/agent/resource_guard.py +90 -0
- codenib/agent/route_context.py +391 -0
- codenib/agent/runner.py +2005 -0
- codenib/agent/runtime/__init__.py +16 -0
- codenib/agent/runtime/context.py +189 -0
- codenib/agent/runtime/trace.py +101 -0
- codenib/agent/skills/__init__.py +25 -0
- codenib/agent/skills/_graphnav.py +235 -0
- codenib/agent/skills/bm25_search/__init__.py +0 -0
- codenib/agent/skills/bm25_search/config.yaml +63 -0
- codenib/agent/skills/bm25_search/executor.py +80 -0
- codenib/agent/skills/bm25_search/skill.md +47 -0
- codenib/agent/skills/code_to_query/__init__.py +0 -0
- codenib/agent/skills/code_to_query/config.yaml +38 -0
- codenib/agent/skills/code_to_query/executor.py +69 -0
- codenib/agent/skills/code_to_query/skill.md +7 -0
- codenib/agent/skills/codenib_context/__init__.py +0 -0
- codenib/agent/skills/codenib_context/config.yaml +44 -0
- codenib/agent/skills/codenib_context/executor.py +184 -0
- codenib/agent/skills/codenib_context/skill.md +21 -0
- codenib/agent/skills/context.py +107 -0
- codenib/agent/skills/core.py +153 -0
- codenib/agent/skills/crossencoder_rerank/__init__.py +3 -0
- codenib/agent/skills/crossencoder_rerank/config.yaml +41 -0
- codenib/agent/skills/crossencoder_rerank/executor.py +62 -0
- codenib/agent/skills/crossencoder_rerank/skill.md +7 -0
- codenib/agent/skills/embedding_search/__init__.py +0 -0
- codenib/agent/skills/embedding_search/config.yaml +53 -0
- codenib/agent/skills/embedding_search/executor.py +57 -0
- codenib/agent/skills/embedding_search/skill.md +47 -0
- codenib/agent/skills/find_callees/__init__.py +0 -0
- codenib/agent/skills/find_callees/config.yaml +22 -0
- codenib/agent/skills/find_callees/executor.py +23 -0
- codenib/agent/skills/find_callees/skill.md +11 -0
- codenib/agent/skills/find_callers/__init__.py +0 -0
- codenib/agent/skills/find_callers/config.yaml +22 -0
- codenib/agent/skills/find_callers/executor.py +23 -0
- codenib/agent/skills/find_callers/skill.md +11 -0
- codenib/agent/skills/hybrid_search/__init__.py +0 -0
- codenib/agent/skills/hybrid_search/config.yaml +38 -0
- codenib/agent/skills/hybrid_search/executor.py +132 -0
- codenib/agent/skills/hybrid_search/skill.md +53 -0
- codenib/agent/skills/llm_rerank/__init__.py +0 -0
- codenib/agent/skills/llm_rerank/config.yaml +33 -0
- codenib/agent/skills/llm_rerank/executor.py +37 -0
- codenib/agent/skills/llm_rerank/skill.md +7 -0
- codenib/agent/skills/loader.py +240 -0
- codenib/agent/skills/lsp_definition/config.yaml +35 -0
- codenib/agent/skills/lsp_definition/executor.py +32 -0
- codenib/agent/skills/lsp_definition/skill.md +18 -0
- codenib/agent/skills/lsp_references/config.yaml +40 -0
- codenib/agent/skills/lsp_references/executor.py +34 -0
- codenib/agent/skills/lsp_references/skill.md +17 -0
- codenib/agent/skills/lsp_route/config.yaml +35 -0
- codenib/agent/skills/lsp_route/executor.py +34 -0
- codenib/agent/skills/lsp_route/skill.md +23 -0
- codenib/agent/skills/registry.py +136 -0
- codenib/agent/skills/repository_search/config.yaml +49 -0
- codenib/agent/skills/repository_search/executor.py +512 -0
- codenib/agent/skills/repository_search/skill.md +28 -0
- codenib/agent/skills/trace/__init__.py +0 -0
- codenib/agent/skills/trace/config.yaml +30 -0
- codenib/agent/skills/trace/executor.py +27 -0
- codenib/agent/skills/trace/skill.md +12 -0
- codenib/agent/skills/typecheck.py +302 -0
- codenib/agent/tool_schema.py +158 -0
- codenib/agent/tools/__init__.py +41 -0
- codenib/agent/tools/defaults.py +1045 -0
- codenib/agent/tools/spec.py +103 -0
- codenib/agent/utils.py +149 -0
- codenib/cli.py +1079 -0
- codenib/clients/__init__.py +5 -0
- codenib/clients/claude_agent.py +534 -0
- codenib/clients/codex_agent.py +314 -0
- codenib/code_chunker.py +730 -0
- codenib/code_chunking/__init__.py +113 -0
- codenib/code_chunking/base.py +592 -0
- codenib/code_chunking/cpp_chunker.py +278 -0
- codenib/code_chunking/csharp_chunker.py +210 -0
- codenib/code_chunking/go_chunker.py +213 -0
- codenib/code_chunking/java_chunker.py +188 -0
- codenib/code_chunking/js_chunker.py +251 -0
- codenib/code_chunking/kotlin_chunker.py +253 -0
- codenib/code_chunking/lua_chunker.py +94 -0
- codenib/code_chunking/php_chunker.py +204 -0
- codenib/code_chunking/python_chunker.py +267 -0
- codenib/code_chunking/ruby_chunker.py +198 -0
- codenib/code_chunking/rust_chunker.py +195 -0
- codenib/code_chunking/scala_chunker.py +182 -0
- codenib/code_chunking/swift_chunker.py +182 -0
- codenib/compat_pickle.py +48 -0
- codenib/compiler/__init__.py +93 -0
- codenib/compiler/index_builders.py +868 -0
- codenib/compiler/index_compiler.py +488 -0
- codenib/compiler/manifest.py +221 -0
- codenib/compiler/params.py +126 -0
- codenib/compiler/resources.py +267 -0
- codenib/compiler/skill_context.py +641 -0
- codenib/compiler/snapshot_store.py +311 -0
- codenib/compiler/verification.py +131 -0
- codenib/dataset/__init__.py +21 -0
- codenib/dataset/base.py +78 -0
- codenib/dataset/codenib_base.py +374 -0
- codenib/dataset/codenib_synthesis.py +540 -0
- codenib/dataset/collect/difficulty_classifier.py +511 -0
- codenib/dataset/collect/swebench_sample.py +766 -0
- codenib/dataset/gt_locate.py +831 -0
- codenib/dataset/local_json.py +184 -0
- codenib/dataset/locbench.py +254 -0
- codenib/dataset/swebench.py +344 -0
- codenib/dataset/swebench_multilingual.py +283 -0
- codenib/dataset/synthesize/__init__.py +71 -0
- codenib/dataset/synthesize/_agent.py +125 -0
- codenib/dataset/synthesize/_types.py +146 -0
- codenib/dataset/synthesize/context_loader.py +601 -0
- codenib/dataset/synthesize/query_curator.py +713 -0
- codenib/dataset/synthesize/query_synthesizer.py +546 -0
- codenib/dataset/synthesize/verifier.py +425 -0
- codenib/dataset/synthesize/vocab_guard.py +264 -0
- codenib/dataset/utils.py +380 -0
- codenib/eval/agent_runner/__init__.py +296 -0
- codenib/eval/agent_runner/baseline.py +299 -0
- codenib/eval/agent_runner/batch.py +176 -0
- codenib/eval/agent_runner/contexts.py +135 -0
- codenib/eval/agent_runner/feedback.py +135 -0
- codenib/eval/agent_runner/feedback_summary.py +326 -0
- codenib/eval/agent_runner/format_diagnostics.py +115 -0
- codenib/eval/agent_runner/live_lsp_provider.py +377 -0
- codenib/eval/agent_runner/loc_baseline.py +355 -0
- codenib/eval/agent_runner/lsp_agent_ab.py +577 -0
- codenib/eval/agent_runner/lsp_agent_study.py +408 -0
- codenib/eval/agent_runner/lsp_agent_study_analysis.py +411 -0
- codenib/eval/agent_runner/lsp_agent_study_artifacts.py +413 -0
- codenib/eval/agent_runner/lsp_agent_study_manifest.py +402 -0
- codenib/eval/agent_runner/lsp_agent_study_runner.py +694 -0
- codenib/eval/agent_runner/lsp_baseline.py +251 -0
- codenib/eval/agent_runner/lsp_latency.py +267 -0
- codenib/eval/agent_runner/lsp_provider_cli.py +273 -0
- codenib/eval/agent_runner/lsp_provider_validation.py +594 -0
- codenib/eval/agent_runner/lsp_readiness.py +167 -0
- codenib/eval/agent_runner/lsp_replay_benchmark.py +1072 -0
- codenib/eval/agent_runner/metrics.py +80 -0
- codenib/eval/agent_runner/orchestrator.py +173 -0
- codenib/eval/agent_runner/pareto.py +154 -0
- codenib/eval/agent_runner/prebuilt.py +484 -0
- codenib/eval/agent_runner/preload.py +219 -0
- codenib/eval/agent_runner/promotion.py +172 -0
- codenib/eval/agent_runner/query_sweep.py +432 -0
- codenib/eval/agent_runner/results.py +69 -0
- codenib/eval/agent_runner/scoring.py +134 -0
- codenib/eval/agent_runner/sweep.py +619 -0
- codenib/eval/agent_runner/sweep_config.py +160 -0
- codenib/eval/agent_runner/symbols.py +67 -0
- codenib/eval/agent_runner/trace_summary.py +405 -0
- codenib/eval/agent_runner/verify_expand.py +219 -0
- codenib/eval/artifact_bundle.py +322 -0
- codenib/eval/artifact_integrity.py +332 -0
- codenib/eval/artifact_manifest.py +236 -0
- codenib/eval/experiments/__init__.py +5 -0
- codenib/eval/experiments/lsp_agent_study_policy.py +55 -0
- codenib/eval/loc_agent_runner.py +37 -0
- codenib/eval/reports/__init__.py +5 -0
- codenib/eval/reports/cost_arm_report.py +810 -0
- codenib/eval/retrieval_eval.py +557 -0
- codenib/graph/__init__.py +44 -0
- codenib/graph/backend_alignment.py +184 -0
- codenib/graph/code_graph.py +1218 -0
- codenib/graph/dependency.py +229 -0
- codenib/graph/hierarchy.py +828 -0
- codenib/graph/incremental/__init__.py +15 -0
- codenib/graph/incremental/change_mgr.py +165 -0
- codenib/graph/incremental/graph_patcher.py +139 -0
- codenib/graph/incremental/lsp_client.py +1271 -0
- codenib/graph/incremental/patcher_base.py +1364 -0
- codenib/graph/incremental/patcher_cpp.py +796 -0
- codenib/graph/incremental/patcher_go.py +56 -0
- codenib/graph/incremental/patcher_python.py +43 -0
- codenib/graph/incremental/patcher_rust.py +113 -0
- codenib/graph/incremental/patcher_ts.py +49 -0
- codenib/graph/incremental/subgraph_mgr.py +1028 -0
- codenib/graph/layers.py +325 -0
- codenib/graph/roi_subgraph.py +394 -0
- codenib/graph/setup.py +689 -0
- codenib/graph/traverse_graph.py +237 -0
- codenib/index/__init__.py +42 -0
- codenib/index/embedding/__init__.py +48 -0
- codenib/index/embedding/builders.py +190 -0
- codenib/index/embedding/model_policy.py +62 -0
- codenib/index/embedding/prompt_registry.py +87 -0
- codenib/index/embedding/vector_store.py +1458 -0
- codenib/index/incremental/__init__.py +30 -0
- codenib/index/incremental/chunk_store.py +409 -0
- codenib/index/incremental/embeddings_cache.py +196 -0
- codenib/index/incremental/git_diff.py +221 -0
- codenib/index/incremental/index_updater.py +283 -0
- codenib/index/incremental/state.py +92 -0
- codenib/index/regex_idx/__init__.py +7 -0
- codenib/index/regex_idx/regex_idx.py +150 -0
- codenib/index/rerank/__init__.py +23 -0
- codenib/index/rerank/cross_encoder.py +356 -0
- codenib/index/sparse_idx/__init__.py +9 -0
- codenib/index/sparse_idx/bm25_index.py +724 -0
- codenib/index/trigram/__init__.py +17 -0
- codenib/index/trigram/zoekt_searcher.py +371 -0
- codenib/languages.py +1035 -0
- codenib/llm/__init__.py +33 -0
- codenib/llm/diagnostics.py +170 -0
- codenib/llm/litellm_chat.py +422 -0
- codenib/llm/options.py +139 -0
- codenib/llm/usage.py +182 -0
- codenib/log_utils.py +301 -0
- codenib/ls_index/__init__.py +17 -0
- codenib/ls_index/clangd_decode.py +912 -0
- codenib/ls_index/clangd_indexer.py +1400 -0
- codenib/ls_index/index_quality.py +442 -0
- codenib/ls_index/lsp_graph_decode.py +449 -0
- codenib/ls_index/lsp_indexer.py +246 -0
- codenib/ls_router.py +507 -0
- codenib/mcp/__init__.py +13 -0
- codenib/mcp/__main__.py +9 -0
- codenib/mcp/context.py +211 -0
- codenib/mcp/prompts.py +76 -0
- codenib/mcp/server.py +463 -0
- codenib/mcp/tools/__init__.py +9 -0
- codenib/mcp/tools/dependency.py +56 -0
- codenib/mcp/tools/lsp.py +119 -0
- codenib/mcp/tools/search.py +224 -0
- codenib/model/__init__.py +49 -0
- codenib/model/agentless_pipeline.py +484 -0
- codenib/model/bm25_retrieve_pipeline.py +100 -0
- codenib/model/dense_graph_expand_rerank_pipeline.py +300 -0
- codenib/model/embedding_retrieve_pipeline.py +152 -0
- codenib/model/graph_augmented_rerank_pipeline.py +19 -0
- codenib/model/graph_retrieve_pipeline.py +259 -0
- codenib/model/hybrid_retrieve_pipeline.py +256 -0
- codenib/model/retrieval_planner.py +377 -0
- codenib/model/retrieve_rerank_pipeline.py +1023 -0
- codenib/ops/expand.py +299 -0
- codenib/ops/filter.py +159 -0
- codenib/ops/rerank.py +246 -0
- codenib/ops/retrieve.py +293 -0
- codenib/ops/transform.py +34 -0
- codenib/paths.py +91 -0
- codenib/profiler.py +506 -0
- codenib/repository_filters.py +103 -0
- codenib/repository_summary.py +161 -0
- codenib/scip_interface/__init__.py +120 -0
- codenib/scip_interface/lsp_occurrence_index.py +327 -0
- codenib/scip_interface/rust_analyzer.py +29 -0
- codenib/scip_interface/scip-environment.yml +14 -0
- codenib/scip_interface/scip.proto +890 -0
- codenib/scip_interface/scip_decode_core.py +190 -0
- codenib/scip_interface/scip_decode_csharp.py +143 -0
- codenib/scip_interface/scip_decode_go.py +433 -0
- codenib/scip_interface/scip_decode_java.py +1117 -0
- codenib/scip_interface/scip_decode_php.py +194 -0
- codenib/scip_interface/scip_decode_python.py +400 -0
- codenib/scip_interface/scip_decode_ruby.py +464 -0
- codenib/scip_interface/scip_decode_rust.py +584 -0
- codenib/scip_interface/scip_decode_ts.py +542 -0
- codenib/scip_interface/scip_decode_utils.py +52 -0
- codenib/scip_interface/scip_indexer_base.py +728 -0
- codenib/scip_interface/scip_indexer_csharp.py +166 -0
- codenib/scip_interface/scip_indexer_go.py +164 -0
- codenib/scip_interface/scip_indexer_java.py +201 -0
- codenib/scip_interface/scip_indexer_php.py +548 -0
- codenib/scip_interface/scip_indexer_python.py +500 -0
- codenib/scip_interface/scip_indexer_ruby.py +369 -0
- codenib/scip_interface/scip_indexer_rust.py +183 -0
- codenib/scip_interface/scip_indexer_ts.py +616 -0
- codenib/scip_interface/scip_install.sh +8 -0
- codenib/scip_interface/scip_pb2.py +80 -0
- codenib/search.py +371 -0
- codenib/source_fingerprint.py +198 -0
- codenib/types.py +105 -0
- codenib/utils.py +93 -0
- codenib/web/__init__.py +9 -0
- codenib/web/app.py +467 -0
- codenib/web/codemap.py +753 -0
- codenib/web/commit_window.py +296 -0
- codenib/web/config.py +331 -0
- codenib/web/edge_label.py +332 -0
- codenib/web/frontend/assets/AskBar-Q_2zSw_D.js +31 -0
- codenib/web/frontend/assets/CodeGraph-C5zIOx4j.js +3 -0
- codenib/web/frontend/assets/CodePanel-BE7Wg1wI.js +1 -0
- codenib/web/frontend/assets/Codemap-C5Xm7Go_.js +2 -0
- codenib/web/frontend/assets/GraphView-CBBbxS9a.js +2 -0
- codenib/web/frontend/assets/Header-CUW7gfQl.js +1 -0
- codenib/web/frontend/assets/HighlightedBlock-H7FGwbAI.js +1 -0
- codenib/web/frontend/assets/HighlightedCode-CTRFPFV9.js +2 -0
- codenib/web/frontend/assets/Mermaid-NhydXLyh.js +303 -0
- codenib/web/frontend/assets/api-DMyba6Hn.js +1 -0
- codenib/web/frontend/assets/arc-Cj6TUG7b.js +1 -0
- codenib/web/frontend/assets/architectureDiagram-3BPJPVTR-BXC7SbpZ.js +36 -0
- codenib/web/frontend/assets/blockDiagram-GPEHLZMM-BYhhgH_O.js +132 -0
- codenib/web/frontend/assets/c4Diagram-AAUBKEIU-BPe9Xb1K.js +10 -0
- codenib/web/frontend/assets/channel-DGptmZnr.js +1 -0
- codenib/web/frontend/assets/chunk-2J33WTMH-mJuoTx1I.js +1 -0
- codenib/web/frontend/assets/chunk-4BX2VUAB-DyIPfnHV.js +1 -0
- codenib/web/frontend/assets/chunk-55IACEB6-C8bSn9Qq.js +1 -0
- codenib/web/frontend/assets/chunk-727SXJPM-CgiBqZFm.js +206 -0
- codenib/web/frontend/assets/chunk-AQP2D5EJ-DOOlLotN.js +231 -0
- codenib/web/frontend/assets/chunk-FMBD7UC4-mf47mSz2.js +15 -0
- codenib/web/frontend/assets/chunk-ND2GUHAM-D6lobpla.js +1 -0
- codenib/web/frontend/assets/chunk-QZHKN3VN-D9a00sWs.js +1 -0
- codenib/web/frontend/assets/classDiagram-4FO5ZUOK-wDtvnBsq.js +1 -0
- codenib/web/frontend/assets/classDiagram-v2-Q7XG4LA2-wDtvnBsq.js +1 -0
- codenib/web/frontend/assets/cose-bilkent-S5V4N54A-JwC1FU9v.js +1 -0
- codenib/web/frontend/assets/cytoscape.esm-CkSuTymj.js +321 -0
- codenib/web/frontend/assets/dagre-BM42HDAG-BTm6Sb-x.js +4 -0
- codenib/web/frontend/assets/defaultLocale-DX6XiGOO.js +1 -0
- codenib/web/frontend/assets/diagram-2AECGRRQ-C7XNwfvk.js +43 -0
- codenib/web/frontend/assets/diagram-5GNKFQAL-DcLriIgZ.js +10 -0
- codenib/web/frontend/assets/diagram-KO2AKTUF-Bo-6Bpfo.js +3 -0
- codenib/web/frontend/assets/diagram-LMA3HP47-M4Nh-qq-.js +24 -0
- codenib/web/frontend/assets/diagram-OG6HWLK6-BUT1QVEf.js +24 -0
- codenib/web/frontend/assets/erDiagram-TEJ5UH35-DViEMoeP.js +85 -0
- codenib/web/frontend/assets/flowDiagram-I6XJVG4X-DjBB1gSM.js +162 -0
- codenib/web/frontend/assets/ganttDiagram-6RSMTGT7-Drhb89TN.js +292 -0
- codenib/web/frontend/assets/gitGraphDiagram-PVQCEYII-CwqblmoY.js +106 -0
- codenib/web/frontend/assets/graph--OzhPTMs.js +1 -0
- codenib/web/frontend/assets/highlight-CDab0zVI.css +1 -0
- codenib/web/frontend/assets/highlight-CnfLc-V2.js +5 -0
- codenib/web/frontend/assets/index-BHP8c3Tq.js +9 -0
- codenib/web/frontend/assets/index-CKJrqu86.css +1 -0
- codenib/web/frontend/assets/infoDiagram-5YYISTIA-BKq62LUG.js +2 -0
- codenib/web/frontend/assets/init-Gi6I4Gst.js +1 -0
- codenib/web/frontend/assets/ishikawaDiagram-YF4QCWOH-DBAgfp9d.js +70 -0
- codenib/web/frontend/assets/journeyDiagram-JHISSGLW-DwHStEV9.js +139 -0
- codenib/web/frontend/assets/kanban-definition-UN3LZRKU-CWPLLTQn.js +89 -0
- codenib/web/frontend/assets/katex-HP8lGamR.js +257 -0
- codenib/web/frontend/assets/layout-SsrduOYp.js +1 -0
- codenib/web/frontend/assets/linear-DNcGZFnN.js +1 -0
- codenib/web/frontend/assets/mindmap-definition-RKZ34NQL-FZHYTpdO.js +96 -0
- codenib/web/frontend/assets/ordinal-Cboi1Yqb.js +1 -0
- codenib/web/frontend/assets/page-Bn-Q8MBy.js +6 -0
- codenib/web/frontend/assets/page-CESv5Ezw.js +2 -0
- codenib/web/frontend/assets/page-CHsElUu2.js +1 -0
- codenib/web/frontend/assets/page-D1WhIRzi.js +1 -0
- codenib/web/frontend/assets/pieDiagram-4H26LBE5-DKlC7xtb.js +30 -0
- codenib/web/frontend/assets/quadrantDiagram-W4KKPZXB-DldkGUZS.js +7 -0
- codenib/web/frontend/assets/requirementDiagram-4Y6WPE33-Dndb6w-j.js +84 -0
- codenib/web/frontend/assets/sankeyDiagram-5OEKKPKP-B2ewtJAC.js +40 -0
- codenib/web/frontend/assets/sequenceDiagram-3UESZ5HK-2N2KbYFt.js +162 -0
- codenib/web/frontend/assets/stateDiagram-AJRCARHV-CLwUuVlz.js +1 -0
- codenib/web/frontend/assets/stateDiagram-v2-BHNVJYJU-IiZdW_Og.js +1 -0
- codenib/web/frontend/assets/timeline-definition-PNZ67QCA-D_IdllIp.js +120 -0
- codenib/web/frontend/assets/vennDiagram-CIIHVFJN-CSU7pI9M.js +34 -0
- codenib/web/frontend/assets/wardley-L42UT6IY-Bv1Eg1Al.js +161 -0
- codenib/web/frontend/assets/wardleyDiagram-YWT4CUSO-Di3bYwLm.js +78 -0
- codenib/web/frontend/assets/xychartDiagram-2RQKCTM6-CPHWEBiN.js +7 -0
- codenib/web/frontend/codenib-icon.svg +34 -0
- codenib/web/frontend/index.html +29 -0
- codenib/web/frontend/runtime-config.js +1 -0
- codenib/web/launcher.py +252 -0
- codenib/web/local.py +196 -0
- codenib/web/repo_registry.py +561 -0
- codenib/web/schemas.py +329 -0
- codenib/web/static_server.py +213 -0
- codenib/wiki/__init__.py +15 -0
- codenib/wiki/agent_wiki.py +4665 -0
- codenib/wiki/builder.py +849 -0
- codenib/wiki/evidence.py +905 -0
- codenib/wiki/narrator.py +258 -0
- codenib/wiki/outline.py +1432 -0
- codenib/wiki/quality.py +932 -0
- codenib-0.1.0.dist-info/METADATA +293 -0
- codenib-0.1.0.dist-info/RECORD +386 -0
- codenib-0.1.0.dist-info/WHEEL +5 -0
- codenib-0.1.0.dist-info/entry_points.txt +13 -0
- codenib-0.1.0.dist-info/licenses/LICENSE +201 -0
- codenib-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025-2026 CodeNib Contributors
|
|
4
|
+
#
|
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
SCIP decoder for Rust projects.
|
|
9
|
+
|
|
10
|
+
Decodes SCIP index files into CodeGraph format, focusing on:
|
|
11
|
+
- Structs (treated as classes)
|
|
12
|
+
- Impl blocks and methods
|
|
13
|
+
- Free functions
|
|
14
|
+
- Traits (treated as classes)
|
|
15
|
+
"""
|
|
16
|
+
import re
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import tomllib
|
|
20
|
+
except ModuleNotFoundError:
|
|
21
|
+
import tomli as tomllib
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from ..graph.code_graph import CodeGraph
|
|
25
|
+
from ..log_utils import get_logger, register_scip_logger
|
|
26
|
+
from ..types import (
|
|
27
|
+
EDGE_TYPE_CONTAIN,
|
|
28
|
+
NODE_TYPE_CLASS,
|
|
29
|
+
NODE_TYPE_FIELD,
|
|
30
|
+
NODE_TYPE_FUNCTION,
|
|
31
|
+
NODE_TYPE_METHOD,
|
|
32
|
+
ROOT_NODE,
|
|
33
|
+
)
|
|
34
|
+
from .scip_indexer_base import extract_scip_blocks, extract_symbol
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SCIPRustGraphDecoder:
|
|
38
|
+
"""
|
|
39
|
+
Decoder for Rust SCIP indices.
|
|
40
|
+
|
|
41
|
+
Parses decoded SCIP index files and builds a CodeGraph representing:
|
|
42
|
+
- Structs as classes
|
|
43
|
+
- Traits as classes
|
|
44
|
+
- Impl blocks and their methods
|
|
45
|
+
- Free functions
|
|
46
|
+
- References between symbols
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, index_file_path, project_root=None):
|
|
50
|
+
"""
|
|
51
|
+
Initialize the Rust SCIP decoder.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
index_file_path: Path to the decoded SCIP index file
|
|
55
|
+
project_root: Root directory of the Rust project
|
|
56
|
+
"""
|
|
57
|
+
self.index_file_path = index_file_path
|
|
58
|
+
self.project_root = Path(project_root) if project_root else None
|
|
59
|
+
self.code_graph = CodeGraph(project_root)
|
|
60
|
+
self.indexed_directories = set()
|
|
61
|
+
self.logger = get_logger(__name__)
|
|
62
|
+
# Register this module for SCIP debug logging
|
|
63
|
+
register_scip_logger(__name__)
|
|
64
|
+
|
|
65
|
+
# Internal workspace crate names (loaded lazily on first document)
|
|
66
|
+
self.internal_crates = None
|
|
67
|
+
|
|
68
|
+
def decode(self):
|
|
69
|
+
"""
|
|
70
|
+
Decode the SCIP index and build the CodeGraph.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
CodeGraph: The constructed code graph
|
|
74
|
+
"""
|
|
75
|
+
self.logger.info(f"Starting SCIP Rust decode from {self.index_file_path}")
|
|
76
|
+
try:
|
|
77
|
+
with open(self.index_file_path, "r") as f:
|
|
78
|
+
content = f.read()
|
|
79
|
+
except Exception as e:
|
|
80
|
+
self.logger.error(f"Error reading SCIP index file: {e}")
|
|
81
|
+
raise
|
|
82
|
+
|
|
83
|
+
# Parse documents
|
|
84
|
+
document_blocks = re.findall(
|
|
85
|
+
r"documents\s*{(.*?)(?=documents\s*{|$)", content, re.DOTALL
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Add the root node to the graph
|
|
89
|
+
self.code_graph.add_root_node(ROOT_NODE)
|
|
90
|
+
|
|
91
|
+
# Process all documents. Batch edge insertion: per-edge add_edges is
|
|
92
|
+
# O(E^2) (igraph reindexes each call); buffering flushes once as O(E).
|
|
93
|
+
with self.code_graph.batch_edges():
|
|
94
|
+
for document in document_blocks:
|
|
95
|
+
self._process_document(document)
|
|
96
|
+
|
|
97
|
+
return self.code_graph
|
|
98
|
+
|
|
99
|
+
def _process_document(self, document_text):
|
|
100
|
+
"""
|
|
101
|
+
Process a single document block from the SCIP index.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
document_text: Text content of the document block
|
|
105
|
+
"""
|
|
106
|
+
# Extract file path
|
|
107
|
+
file_match = re.search(r'relative_path:\s*"([^"]+)"', document_text)
|
|
108
|
+
if not file_match:
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
file_path = file_match.group(1)
|
|
112
|
+
|
|
113
|
+
# Only process Rust files
|
|
114
|
+
if not file_path.endswith(".rs"):
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
# Add directory hierarchy
|
|
118
|
+
dir_path = Path(file_path).parent
|
|
119
|
+
while dir_path != dir_path.parent: # Stop at the root directory
|
|
120
|
+
dir_path_str = str(dir_path)
|
|
121
|
+
if dir_path_str not in self.indexed_directories:
|
|
122
|
+
self.code_graph.add_directory_node(dir_path_str)
|
|
123
|
+
self.indexed_directories.add(dir_path_str)
|
|
124
|
+
# Add containment edge from parent directory
|
|
125
|
+
self.code_graph._add_edge(
|
|
126
|
+
str(dir_path.parent), dir_path_str, EDGE_TYPE_CONTAIN
|
|
127
|
+
)
|
|
128
|
+
dir_path = dir_path.parent
|
|
129
|
+
|
|
130
|
+
# Add file node
|
|
131
|
+
self.code_graph.add_file_node(file_path)
|
|
132
|
+
|
|
133
|
+
# Add file containment edge
|
|
134
|
+
self.code_graph._add_edge(
|
|
135
|
+
str(Path(file_path).parent), file_path, EDGE_TYPE_CONTAIN
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Load workspace crate names once (for third-party filtering)
|
|
139
|
+
if self.internal_crates is None:
|
|
140
|
+
self.internal_crates = self._load_workspace_crates()
|
|
141
|
+
|
|
142
|
+
# Process occurrences
|
|
143
|
+
# Brace-balanced: see extract_scip_blocks docstring (regex truncates
|
|
144
|
+
# on symbols containing literal "{" / "}").
|
|
145
|
+
occurrences = extract_scip_blocks(document_text, "occurrences")
|
|
146
|
+
for occurrence in occurrences:
|
|
147
|
+
self._process_occurrence(occurrence, file_path)
|
|
148
|
+
|
|
149
|
+
def _load_workspace_crates(self):
|
|
150
|
+
"""
|
|
151
|
+
Parse Cargo.toml to extract internal workspace crate names.
|
|
152
|
+
|
|
153
|
+
Reads [workspace.members] glob patterns, resolves them to directories,
|
|
154
|
+
then reads each member's Cargo.toml to get [package].name.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Set of internal crate names, or empty set if parsing fails.
|
|
158
|
+
"""
|
|
159
|
+
if not self.project_root:
|
|
160
|
+
return set()
|
|
161
|
+
|
|
162
|
+
cargo_toml_path = self.project_root / "Cargo.toml"
|
|
163
|
+
if not cargo_toml_path.exists():
|
|
164
|
+
return set()
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
with open(cargo_toml_path, "rb") as f:
|
|
168
|
+
cargo_data = tomllib.load(f)
|
|
169
|
+
except Exception as e:
|
|
170
|
+
self.logger.warning(f"Failed to parse {cargo_toml_path}: {e}")
|
|
171
|
+
return set()
|
|
172
|
+
|
|
173
|
+
# Get workspace member patterns
|
|
174
|
+
workspace = cargo_data.get("workspace", {})
|
|
175
|
+
member_patterns = workspace.get("members", [])
|
|
176
|
+
if not member_patterns:
|
|
177
|
+
# Single-crate project: use [package].name
|
|
178
|
+
pkg_name = cargo_data.get("package", {}).get("name")
|
|
179
|
+
return {pkg_name} if pkg_name else set()
|
|
180
|
+
|
|
181
|
+
# Resolve glob patterns to actual member directories
|
|
182
|
+
internal_crates = set()
|
|
183
|
+
|
|
184
|
+
# Include the root package itself (e.g. ripgrep defines both
|
|
185
|
+
# [package] name="ripgrep" and [workspace] members=["crates/*"]).
|
|
186
|
+
root_pkg_name = cargo_data.get("package", {}).get("name")
|
|
187
|
+
if root_pkg_name:
|
|
188
|
+
internal_crates.add(root_pkg_name)
|
|
189
|
+
|
|
190
|
+
for pattern in member_patterns:
|
|
191
|
+
# Malformed member globs ("", trailing slash, absolute paths) make
|
|
192
|
+
# Path.glob raise on Python 3.12; skip them instead of aborting.
|
|
193
|
+
pattern = str(pattern).strip().rstrip("/")
|
|
194
|
+
if not pattern or pattern.startswith("/"):
|
|
195
|
+
continue
|
|
196
|
+
try:
|
|
197
|
+
members = list(self.project_root.glob(pattern))
|
|
198
|
+
except (ValueError, IndexError, OSError):
|
|
199
|
+
continue
|
|
200
|
+
for member_dir in members:
|
|
201
|
+
if not member_dir.is_dir():
|
|
202
|
+
continue
|
|
203
|
+
member_cargo = member_dir / "Cargo.toml"
|
|
204
|
+
if not member_cargo.exists():
|
|
205
|
+
continue
|
|
206
|
+
try:
|
|
207
|
+
with open(member_cargo, "rb") as f:
|
|
208
|
+
member_data = tomllib.load(f)
|
|
209
|
+
crate_name = member_data.get("package", {}).get("name")
|
|
210
|
+
if crate_name:
|
|
211
|
+
internal_crates.add(crate_name)
|
|
212
|
+
except Exception:
|
|
213
|
+
continue
|
|
214
|
+
|
|
215
|
+
self.logger.info(f"Loaded {len(internal_crates)} internal workspace crates")
|
|
216
|
+
return internal_crates
|
|
217
|
+
|
|
218
|
+
def _process_occurrence(self, occurrence_text, file_path):
|
|
219
|
+
"""
|
|
220
|
+
Process a single occurrence from the SCIP index.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
occurrence_text: Text content of the occurrence
|
|
224
|
+
file_path: Path to the file containing this occurrence
|
|
225
|
+
"""
|
|
226
|
+
# Extract ranges
|
|
227
|
+
ranges = re.findall(r"range:\s*(\d+)", occurrence_text)
|
|
228
|
+
if len(ranges) < 3:
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
line = int(ranges[0])
|
|
232
|
+
col_start = int(ranges[1])
|
|
233
|
+
col_end = int(ranges[2])
|
|
234
|
+
|
|
235
|
+
# Extract symbol (unescape-aware; see extract_symbol docstring).
|
|
236
|
+
symbol = extract_symbol(occurrence_text)
|
|
237
|
+
if not symbol:
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
# Skip local symbols (anonymous/unnamed)
|
|
241
|
+
if "local " in symbol:
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
# Skip module symbols — last SCIP descriptor ends with "/" (e.g. "linter/", "crate/").
|
|
245
|
+
# In Rust SCIP, "/" suffix is exclusively used for Module kind.
|
|
246
|
+
if symbol.split()[-1].endswith("/"):
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
# Filter symbols by origin
|
|
250
|
+
# Format: "rust-analyzer <manager> <crate_name> ..."
|
|
251
|
+
# cargo <crate> -> internal or third-party crate
|
|
252
|
+
# crate -> current project crate (always keep)
|
|
253
|
+
# rust-lang/rust -> stdlib (always skip)
|
|
254
|
+
parts = symbol.split(" ")
|
|
255
|
+
if len(parts) >= 3:
|
|
256
|
+
if parts[1] == "cargo":
|
|
257
|
+
if self.internal_crates and parts[2] not in self.internal_crates:
|
|
258
|
+
return
|
|
259
|
+
elif parts[1] != "crate":
|
|
260
|
+
return
|
|
261
|
+
|
|
262
|
+
# Extract symbol_roles
|
|
263
|
+
symbol_roles_match = re.search(r"symbol_roles:\s*(\d+)", occurrence_text)
|
|
264
|
+
if symbol_roles_match:
|
|
265
|
+
symbol_roles = int(symbol_roles_match.group(1))
|
|
266
|
+
else:
|
|
267
|
+
symbol_roles = 0 # Reference
|
|
268
|
+
|
|
269
|
+
# Extract enclosing range if available
|
|
270
|
+
enclosing_ranges = re.findall(r"enclosing_range:\s*(\d+)", occurrence_text)
|
|
271
|
+
|
|
272
|
+
# Process the symbol
|
|
273
|
+
self._process_symbol(
|
|
274
|
+
symbol, file_path, line, col_start, col_end, symbol_roles, enclosing_ranges
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
def _extract_impl_display(self, impl_member, symbol_type=None):
|
|
278
|
+
"""Extract display name from impl pattern like [ImplType][Trait]method.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
impl_member: Everything after 'impl#', e.g. '[VecBuffer][Buffer]snapshot'
|
|
282
|
+
or '[Error][From<io::Error>]from'
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
Display string like 'VecBuffer.snapshot()' or 'Error<From<io::Error>>.from()'
|
|
286
|
+
"""
|
|
287
|
+
impl_member = impl_member.replace("`", "")
|
|
288
|
+
|
|
289
|
+
# Parse bracket segments: [ImplType], [Trait], then remaining method
|
|
290
|
+
brackets = []
|
|
291
|
+
rest = impl_member
|
|
292
|
+
while rest.startswith("["):
|
|
293
|
+
# Find matching close bracket (handle nested <> inside)
|
|
294
|
+
depth = 0
|
|
295
|
+
end = -1
|
|
296
|
+
for i, ch in enumerate(rest):
|
|
297
|
+
if ch == "[":
|
|
298
|
+
depth += 1
|
|
299
|
+
elif ch == "]":
|
|
300
|
+
depth -= 1
|
|
301
|
+
if depth == 0:
|
|
302
|
+
end = i
|
|
303
|
+
break
|
|
304
|
+
if end == -1:
|
|
305
|
+
break
|
|
306
|
+
brackets.append(rest[1:end]) # content inside [ ]
|
|
307
|
+
rest = rest[end + 1 :]
|
|
308
|
+
|
|
309
|
+
# brackets[0] = ImplType, brackets[1] = Trait (if present)
|
|
310
|
+
impl_type = brackets[0] if brackets else ""
|
|
311
|
+
trait_name = brackets[1] if len(brackets) > 1 else ""
|
|
312
|
+
method_name = rest
|
|
313
|
+
|
|
314
|
+
# Remove lifetime-only generics (e.g. <'a> or <'a, 'db>)
|
|
315
|
+
# Note: escaped quotes (\\') appear in SCIP symbols
|
|
316
|
+
trait_name = re.sub(r"^<['\\'a-z_,\s]+>$", "", trait_name)
|
|
317
|
+
impl_type = re.sub(r"<['\\'a-z_,\s]+>", "", impl_type)
|
|
318
|
+
|
|
319
|
+
# Build display: ImplType[<Trait>].method()
|
|
320
|
+
if trait_name:
|
|
321
|
+
type_display = f"{impl_type}<{trait_name}>"
|
|
322
|
+
else:
|
|
323
|
+
type_display = impl_type
|
|
324
|
+
|
|
325
|
+
if method_name:
|
|
326
|
+
suffix = "" if symbol_type == NODE_TYPE_FIELD else "()"
|
|
327
|
+
return f"{type_display}.{method_name}{suffix}"
|
|
328
|
+
else:
|
|
329
|
+
return type_display
|
|
330
|
+
|
|
331
|
+
def _extract_symbol_display(self, unified_symbol, symbol_type=None):
|
|
332
|
+
"""Extract human-readable symbol display from the unified_symbol key.
|
|
333
|
+
|
|
334
|
+
The unified_symbol key is in SCIP format: crate/module/Type#member
|
|
335
|
+
This extracts just the symbol part (Type.member) without crate/module prefix.
|
|
336
|
+
|
|
337
|
+
Examples:
|
|
338
|
+
ruff_linter/Checker#check → Checker.check()
|
|
339
|
+
ruff_linter/check_path → check_path()
|
|
340
|
+
ruff_linter/Checker → Checker
|
|
341
|
+
ruff_linter/SourceKind#Jupyter → SourceKind.Jupyter
|
|
342
|
+
ruff_linter/impl#[VecBuffer][Buffer]snapshot → VecBuffer.snapshot()
|
|
343
|
+
"""
|
|
344
|
+
sym = unified_symbol
|
|
345
|
+
|
|
346
|
+
if "#" in sym:
|
|
347
|
+
type_path, member = sym.split("#", 1)
|
|
348
|
+
type_name = type_path.rsplit("/", 1)[-1] # last segment before #
|
|
349
|
+
|
|
350
|
+
if type_name == "impl":
|
|
351
|
+
return self._extract_impl_display(member, symbol_type)
|
|
352
|
+
|
|
353
|
+
# Clean backticks and lifetime-only generics from member
|
|
354
|
+
member = member.replace("`", "")
|
|
355
|
+
member = re.sub(r"<['\\'a-z_,\s]+>", "", member)
|
|
356
|
+
|
|
357
|
+
# Nested type: Type#Variant# → member still has trailing content
|
|
358
|
+
# Strip trailing # from member (e.g. enum variant Type#Variant#)
|
|
359
|
+
member = member.rstrip("#")
|
|
360
|
+
|
|
361
|
+
if member:
|
|
362
|
+
suffix = "" if symbol_type == NODE_TYPE_FIELD else "()"
|
|
363
|
+
return f"{type_name}.{member}{suffix}"
|
|
364
|
+
else:
|
|
365
|
+
return type_name
|
|
366
|
+
else:
|
|
367
|
+
func_name = sym.rsplit("/", 1)[-1]
|
|
368
|
+
func_name = func_name.replace("`", "")
|
|
369
|
+
if symbol_type in (NODE_TYPE_FUNCTION, NODE_TYPE_METHOD):
|
|
370
|
+
return f"{func_name}()"
|
|
371
|
+
return func_name
|
|
372
|
+
|
|
373
|
+
def _get_unified_name(self, unified_symbol, file_path, symbol_type=None):
|
|
374
|
+
"""Generate unified_name in format file_path:SymbolDisplay.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
unified_symbol: The name key (e.g. 'ruff_linter/Checker#check')
|
|
378
|
+
file_path: File path of the symbol (e.g. 'crates/ruff_linter/src/checker.rs')
|
|
379
|
+
symbol_type: NODE_TYPE_* constant
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
Unified name like 'crates/ruff_linter/src/checker.rs:Checker.check()'
|
|
383
|
+
"""
|
|
384
|
+
symbol_display = self._extract_symbol_display(unified_symbol, symbol_type)
|
|
385
|
+
if file_path and symbol_display:
|
|
386
|
+
return f"{file_path}:{symbol_display}"
|
|
387
|
+
return file_path or symbol_display or unified_symbol
|
|
388
|
+
|
|
389
|
+
def _unify_symbol_name(self, symbol, file_path):
|
|
390
|
+
"""
|
|
391
|
+
Unify Rust symbol names, preserving SCIP format for uniqueness.
|
|
392
|
+
Prepends crate name to avoid cross-crate collisions in workspace projects.
|
|
393
|
+
|
|
394
|
+
Examples:
|
|
395
|
+
- rust-analyzer cargo nu-cli 0.93.1 commands/main().
|
|
396
|
+
-> nu-cli/commands/main
|
|
397
|
+
- rust-analyzer cargo nu-cli 0.93.1 Options#
|
|
398
|
+
-> nu-cli/Options
|
|
399
|
+
- rust-analyzer cargo nu-cli 0.93.1 Options#area().
|
|
400
|
+
-> nu-cli/Options#area
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
symbol: Original symbol name from SCIP
|
|
404
|
+
file_path: File path where the symbol was found (stored as node attribute)
|
|
405
|
+
|
|
406
|
+
Returns:
|
|
407
|
+
Unified symbol name in SCIP format
|
|
408
|
+
(``crate/module/Type#method`` or ``crate/module/function``)
|
|
409
|
+
"""
|
|
410
|
+
# Extract the actual symbol part (after version or URL)
|
|
411
|
+
# Format: "rust-analyzer cargo <crate_name> <version> <symbol_path>"
|
|
412
|
+
# Split into at most 5 parts so that symbol_path (which may contain
|
|
413
|
+
# spaces in generics like `<B, E>`) stays intact.
|
|
414
|
+
parts = symbol.split(" ", 4)
|
|
415
|
+
if len(parts) < 5:
|
|
416
|
+
return None
|
|
417
|
+
|
|
418
|
+
# Extract crate name for scoping
|
|
419
|
+
crate_prefix = parts[2] if parts[1] == "cargo" else None
|
|
420
|
+
|
|
421
|
+
symbol_part = parts[4].rstrip(".")
|
|
422
|
+
|
|
423
|
+
# Clean up trailing markers
|
|
424
|
+
unified = symbol_part.rstrip("#/()")
|
|
425
|
+
|
|
426
|
+
# Prepend crate name to avoid cross-crate collisions
|
|
427
|
+
if crate_prefix:
|
|
428
|
+
unified = f"{crate_prefix}/{unified}"
|
|
429
|
+
|
|
430
|
+
return unified
|
|
431
|
+
|
|
432
|
+
def _classify_symbol_type(self, unified_symbol, original_symbol):
|
|
433
|
+
"""
|
|
434
|
+
Classify the symbol type based on Rust conventions.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
unified_symbol: Unified symbol name in SCIP format (like "shapes/Rectangle#area")
|
|
438
|
+
original_symbol: Original symbol from SCIP
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
Symbol type constant (NODE_TYPE_CLASS, NODE_TYPE_METHOD, NODE_TYPE_FUNCTION)
|
|
442
|
+
"""
|
|
443
|
+
# Member: has # with something after it
|
|
444
|
+
if "#" in unified_symbol:
|
|
445
|
+
parts = unified_symbol.split("#")
|
|
446
|
+
if len(parts) > 1 and parts[1]:
|
|
447
|
+
# Distinguish field vs method using original SCIP symbol:
|
|
448
|
+
# method ends with "()." (e.g. "impl#[Type]method().")
|
|
449
|
+
# field ends with "." (e.g. "Type#field.")
|
|
450
|
+
if original_symbol and original_symbol.endswith("()."):
|
|
451
|
+
return NODE_TYPE_METHOD
|
|
452
|
+
else:
|
|
453
|
+
return NODE_TYPE_FIELD
|
|
454
|
+
else:
|
|
455
|
+
# Just type (ends with #)
|
|
456
|
+
return NODE_TYPE_CLASS
|
|
457
|
+
|
|
458
|
+
# Check last component to determine type
|
|
459
|
+
last_component = unified_symbol.split("/")[-1]
|
|
460
|
+
|
|
461
|
+
# Struct/Trait: starts with uppercase
|
|
462
|
+
if last_component and last_component[0].isupper():
|
|
463
|
+
return NODE_TYPE_CLASS
|
|
464
|
+
|
|
465
|
+
# Default to function
|
|
466
|
+
return NODE_TYPE_FUNCTION
|
|
467
|
+
|
|
468
|
+
def _process_symbol(
|
|
469
|
+
self,
|
|
470
|
+
symbol,
|
|
471
|
+
file_path,
|
|
472
|
+
line,
|
|
473
|
+
col_start,
|
|
474
|
+
col_end,
|
|
475
|
+
symbol_roles,
|
|
476
|
+
enclosing_ranges,
|
|
477
|
+
):
|
|
478
|
+
"""
|
|
479
|
+
Process a symbol occurrence and add it to the graph.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
symbol: Symbol name from SCIP
|
|
483
|
+
file_path: File containing the symbol
|
|
484
|
+
line: Line number
|
|
485
|
+
col_start: Column start
|
|
486
|
+
col_end: Column end
|
|
487
|
+
symbol_roles: Symbol roles (1=definition, 0=reference)
|
|
488
|
+
enclosing_ranges: Enclosing scope ranges
|
|
489
|
+
"""
|
|
490
|
+
self.logger.scip_debug(
|
|
491
|
+
f"Processing Rust symbol: {symbol} at {file_path}:{line}, roles: {symbol_roles}"
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
# Unify symbol name
|
|
495
|
+
unified_symbol = self._unify_symbol_name(symbol, file_path)
|
|
496
|
+
if not unified_symbol:
|
|
497
|
+
return
|
|
498
|
+
|
|
499
|
+
# Classify symbol type
|
|
500
|
+
symbol_type = self._classify_symbol_type(unified_symbol, symbol)
|
|
501
|
+
|
|
502
|
+
# Exit scopes based on current line
|
|
503
|
+
try:
|
|
504
|
+
self.code_graph.exit_scopes_by_line(line)
|
|
505
|
+
except Exception as e:
|
|
506
|
+
self.logger.error(f"Error exiting scopes at line {line}: {e}")
|
|
507
|
+
raise
|
|
508
|
+
|
|
509
|
+
# Handle definition (check if definition bit is set)
|
|
510
|
+
is_definition = symbol_roles & 1 # Bitwise AND to check definition bit
|
|
511
|
+
if is_definition:
|
|
512
|
+
if enclosing_ranges and len(enclosing_ranges) >= 4:
|
|
513
|
+
# Multi-line enclosing range: [start_line, start_col, end_line, end_col]
|
|
514
|
+
scope_start_line = int(enclosing_ranges[0])
|
|
515
|
+
scope_end_line = int(enclosing_ranges[2])
|
|
516
|
+
|
|
517
|
+
self.code_graph.add_symbol_node(
|
|
518
|
+
unified_symbol, line, scope_start_line, scope_end_line, symbol_type
|
|
519
|
+
)
|
|
520
|
+
if unified_symbol in self.code_graph.name_to_vertex:
|
|
521
|
+
vertex_id = self.code_graph.name_to_vertex[unified_symbol]
|
|
522
|
+
self.code_graph.graph.vs[vertex_id]["unified_name"] = (
|
|
523
|
+
self._get_unified_name(unified_symbol, file_path, symbol_type)
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
self.code_graph.add_containment_edge(unified_symbol)
|
|
527
|
+
|
|
528
|
+
if symbol_type in [
|
|
529
|
+
NODE_TYPE_CLASS,
|
|
530
|
+
NODE_TYPE_FUNCTION,
|
|
531
|
+
NODE_TYPE_METHOD,
|
|
532
|
+
]:
|
|
533
|
+
self.code_graph.update_current_scope(
|
|
534
|
+
unified_symbol, scope_start_line, scope_end_line
|
|
535
|
+
)
|
|
536
|
+
elif enclosing_ranges and len(enclosing_ranges) == 3:
|
|
537
|
+
# Single-line enclosing range: [line, col_start, col_end]
|
|
538
|
+
# Fill range into node but don't push onto scope stack
|
|
539
|
+
enc_line = int(enclosing_ranges[0])
|
|
540
|
+
self.code_graph.add_symbol_node(
|
|
541
|
+
unified_symbol, line, enc_line, enc_line, symbol_type
|
|
542
|
+
)
|
|
543
|
+
if unified_symbol in self.code_graph.name_to_vertex:
|
|
544
|
+
vertex_id = self.code_graph.name_to_vertex[unified_symbol]
|
|
545
|
+
self.code_graph.graph.vs[vertex_id]["unified_name"] = (
|
|
546
|
+
self._get_unified_name(unified_symbol, file_path, symbol_type)
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
self.code_graph.add_containment_edge(unified_symbol)
|
|
550
|
+
else:
|
|
551
|
+
# No enclosing range (stable rust-analyzer)
|
|
552
|
+
self.code_graph.add_symbol_node(
|
|
553
|
+
unified_symbol, line, symbol_type=symbol_type
|
|
554
|
+
)
|
|
555
|
+
if unified_symbol in self.code_graph.name_to_vertex:
|
|
556
|
+
vertex_id = self.code_graph.name_to_vertex[unified_symbol]
|
|
557
|
+
self.code_graph.graph.vs[vertex_id]["unified_name"] = (
|
|
558
|
+
self._get_unified_name(unified_symbol, file_path, symbol_type)
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
self.code_graph._add_edge(
|
|
562
|
+
self.code_graph.current_scope, unified_symbol, EDGE_TYPE_CONTAIN
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
# Handle reference (not a definition)
|
|
566
|
+
else:
|
|
567
|
+
self.code_graph.add_symbol_reference(
|
|
568
|
+
unified_symbol, file_path, symbol_type, anchor_line=line
|
|
569
|
+
)
|
|
570
|
+
if unified_symbol in self.code_graph.name_to_vertex:
|
|
571
|
+
vid = self.code_graph.name_to_vertex[unified_symbol]
|
|
572
|
+
if not self.code_graph.graph.vs[vid].attributes().get("unified_name"):
|
|
573
|
+
self.code_graph.graph.vs[vid]["unified_name"] = (
|
|
574
|
+
self._get_unified_name(unified_symbol, file_path, symbol_type)
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
def save_graph(self, output_path):
|
|
578
|
+
"""
|
|
579
|
+
Save the code graph to a file.
|
|
580
|
+
|
|
581
|
+
Args:
|
|
582
|
+
output_path: Path where the graph should be saved
|
|
583
|
+
"""
|
|
584
|
+
self.code_graph.save_graph(output_path)
|