mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-06 01:54:11 +08:00
- Add `association_tree` module with components for building and processing call association trees using LSP call hierarchy capabilities. - Introduce `AssociationTreeBuilder` for constructing call trees from seed locations with depth-first expansion. - Create data structures: `TreeNode`, `CallTree`, and `UniqueNode` for representing nodes and relationships in the call tree. - Implement `ResultDeduplicator` to extract unique nodes from call trees and assign relevance scores based on depth, frequency, and kind. - Add unit tests for `AssociationTreeBuilder` and `ResultDeduplicator` to ensure functionality and correctness.
157 lines
4.5 KiB
Python
157 lines
4.5 KiB
Python
"""Demo script for association tree building.
|
|
|
|
This script demonstrates how to use the AssociationTreeBuilder and
|
|
ResultDeduplicator to explore code relationships via LSP call hierarchy.
|
|
"""
|
|
|
|
import asyncio
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Add parent directory to path for imports
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
|
|
from codexlens.lsp.standalone_manager import StandaloneLspManager
|
|
from codexlens.search.association_tree import (
|
|
AssociationTreeBuilder,
|
|
ResultDeduplicator,
|
|
)
|
|
|
|
|
|
async def demo_simple_tree():
|
|
"""Build a simple call tree from a Python file."""
|
|
print("=" * 70)
|
|
print("Association Tree Demo")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
# Use this file as the test subject
|
|
test_file = Path(__file__).resolve()
|
|
workspace_root = test_file.parent.parent
|
|
|
|
print(f"Workspace: {workspace_root}")
|
|
print(f"Test file: {test_file.name}")
|
|
print()
|
|
|
|
# Initialize LSP manager
|
|
async with StandaloneLspManager(
|
|
workspace_root=str(workspace_root),
|
|
timeout=10.0,
|
|
) as lsp:
|
|
print("LSP manager initialized")
|
|
print()
|
|
|
|
# Create tree builder
|
|
builder = AssociationTreeBuilder(lsp, timeout=5.0)
|
|
|
|
# Build tree from a function in this file
|
|
# Using line 50 as an example (adjust based on actual file)
|
|
print(f"Building call tree from {test_file.name}:50...")
|
|
tree = await builder.build_tree(
|
|
seed_file_path=str(test_file),
|
|
seed_line=50,
|
|
seed_character=1,
|
|
max_depth=3,
|
|
expand_callers=True,
|
|
expand_callees=True,
|
|
)
|
|
|
|
print(f"Tree built: {tree}")
|
|
print(f" Roots: {len(tree.roots)}")
|
|
print(f" Total unique nodes: {len(tree.all_nodes)}")
|
|
print(f" Total node instances: {len(tree.node_list)}")
|
|
print(f" Edges: {len(tree.edges)}")
|
|
print()
|
|
|
|
if tree.roots:
|
|
print("Root nodes:")
|
|
for root in tree.roots:
|
|
print(f" - {root.item.name} ({root.item.kind})")
|
|
print(f" {root.item.file_path}:{root.item.range.start_line}")
|
|
print()
|
|
|
|
# Deduplicate and score
|
|
print("Deduplicating and scoring nodes...")
|
|
deduplicator = ResultDeduplicator(
|
|
depth_weight=0.4,
|
|
frequency_weight=0.3,
|
|
kind_weight=0.3,
|
|
)
|
|
|
|
unique_nodes = deduplicator.deduplicate(tree, max_results=20)
|
|
print(f"Found {len(unique_nodes)} unique nodes")
|
|
print()
|
|
|
|
if unique_nodes:
|
|
print("Top 10 nodes by score:")
|
|
print("-" * 70)
|
|
for i, node in enumerate(unique_nodes[:10], 1):
|
|
print(f"{i:2}. {node.name} ({node.kind})")
|
|
print(f" Location: {Path(node.file_path).name}:{node.range.start_line}")
|
|
print(
|
|
f" Depth: {node.min_depth}, "
|
|
f"Occurrences: {node.occurrences}, "
|
|
f"Score: {node.score:.3f}"
|
|
)
|
|
if node.paths:
|
|
print(f" Paths: {len(node.paths)}")
|
|
print()
|
|
|
|
# Show filtering capabilities
|
|
functions = deduplicator.filter_by_kind(
|
|
unique_nodes, ["function", "method"]
|
|
)
|
|
print(f"Functions/methods only: {len(functions)} nodes")
|
|
|
|
if functions:
|
|
print("Top 5 functions:")
|
|
for i, node in enumerate(functions[:5], 1):
|
|
print(f" {i}. {node.name} (score: {node.score:.3f})")
|
|
|
|
else:
|
|
print("No nodes found. Try a different seed location.")
|
|
|
|
print()
|
|
print("Demo complete!")
|
|
|
|
|
|
async def demo_cycle_detection():
|
|
"""Demonstrate cycle detection in call trees."""
|
|
print("\n" + "=" * 70)
|
|
print("Cycle Detection Demo")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
# Create a simple Python file with circular calls for testing
|
|
test_code = '''
|
|
def func_a():
|
|
"""Function A calls B."""
|
|
func_b()
|
|
|
|
def func_b():
|
|
"""Function B calls A (creates a cycle)."""
|
|
func_a()
|
|
'''
|
|
|
|
print("This demo would detect cycles in:")
|
|
print(test_code)
|
|
print("The tree builder automatically marks cycle nodes to prevent infinite expansion.")
|
|
|
|
|
|
def main():
|
|
"""Run the demo."""
|
|
try:
|
|
asyncio.run(demo_simple_tree())
|
|
demo_cycle_detection()
|
|
except KeyboardInterrupt:
|
|
print("\nDemo interrupted by user")
|
|
except Exception as e:
|
|
print(f"\nError running demo: {e}")
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|