mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
feat: 更新 LSP 测试,调整测试文件和增加分析等待时间
This commit is contained in:
@@ -8,10 +8,11 @@ from codexlens.search.association_tree import AssociationTreeBuilder
|
|||||||
async def test_lsp_tree():
|
async def test_lsp_tree():
|
||||||
"""Test building LSP association tree for a known Python file."""
|
"""Test building LSP association tree for a known Python file."""
|
||||||
|
|
||||||
# Setup
|
# Setup - use simple test file
|
||||||
workspace_root = Path("D:/Claude_dms3/codex-lens/src")
|
workspace_root = Path("D:/Claude_dms3/codex-lens")
|
||||||
test_file = "codexlens/search/hybrid_search.py"
|
test_file = "test_simple_function.py"
|
||||||
test_line = 115 # search() method definition
|
test_line = 11 # main() function definition (1-based)
|
||||||
|
test_char = 5 # Points to 'm' in 'main' (1-based, becomes 4 in 0-based)
|
||||||
|
|
||||||
print(f"Testing LSP tree for: {test_file}:{test_line}")
|
print(f"Testing LSP tree for: {test_file}:{test_line}")
|
||||||
print("="*80)
|
print("="*80)
|
||||||
@@ -29,11 +30,11 @@ async def test_lsp_tree():
|
|||||||
print(" [OK] LSP manager started")
|
print(" [OK] LSP manager started")
|
||||||
|
|
||||||
# Test get_call_hierarchy_items directly
|
# Test get_call_hierarchy_items directly
|
||||||
print(f"\n2. Testing get_call_hierarchy_items for {test_file}:{test_line}...")
|
print(f"\n2. Testing get_call_hierarchy_items for {test_file}:{test_line}:{test_char}...")
|
||||||
items = await manager.get_call_hierarchy_items(
|
items = await manager.get_call_hierarchy_items(
|
||||||
file_path=str(workspace_root / test_file),
|
file_path=str(workspace_root / test_file),
|
||||||
line=test_line,
|
line=test_line,
|
||||||
character=10,
|
character=test_char,
|
||||||
)
|
)
|
||||||
print(f" Result: {len(items)} items")
|
print(f" Result: {len(items)} items")
|
||||||
if items:
|
if items:
|
||||||
@@ -59,7 +60,7 @@ async def test_lsp_tree():
|
|||||||
tree = await builder.build_tree(
|
tree = await builder.build_tree(
|
||||||
seed_file_path=str(workspace_root / test_file),
|
seed_file_path=str(workspace_root / test_file),
|
||||||
seed_line=test_line,
|
seed_line=test_line,
|
||||||
seed_character=10,
|
seed_character=test_char,
|
||||||
max_depth=2,
|
max_depth=2,
|
||||||
expand_callers=True,
|
expand_callers=True,
|
||||||
expand_callees=True,
|
expand_callees=True,
|
||||||
|
|||||||
99
codex-lens/examples/test_wait_for_analysis.py
Normal file
99
codex-lens/examples/test_wait_for_analysis.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
"""Test with longer wait time for Pyright analysis."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from codexlens.lsp.standalone_manager import StandaloneLspManager
|
||||||
|
|
||||||
|
async def test_with_wait():
|
||||||
|
"""Test prepareCallHierarchy with longer wait for analysis."""
|
||||||
|
|
||||||
|
workspace_root = Path("D:/Claude_dms3/codex-lens")
|
||||||
|
test_file = workspace_root / "test_simple_function.py"
|
||||||
|
|
||||||
|
print("Testing with Wait for Analysis")
|
||||||
|
print("="*80)
|
||||||
|
|
||||||
|
manager = StandaloneLspManager(
|
||||||
|
workspace_root=str(workspace_root),
|
||||||
|
timeout=30.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("\n1. Starting LSP manager...")
|
||||||
|
await manager.start()
|
||||||
|
|
||||||
|
state = await manager._get_server(str(test_file))
|
||||||
|
if not state:
|
||||||
|
print(" [ERROR] No server state")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(" [OK] Server ready")
|
||||||
|
print(f" Workspace: {manager.workspace_root}")
|
||||||
|
|
||||||
|
# Open document
|
||||||
|
print("\n2. Opening document...")
|
||||||
|
await manager._open_document(state, str(test_file))
|
||||||
|
print(" [OK] Document opened")
|
||||||
|
|
||||||
|
# Wait longer for analysis
|
||||||
|
print("\n3. Waiting for Pyright to analyze (5 seconds)...")
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
print(" [OK] Wait complete")
|
||||||
|
|
||||||
|
# Check diagnostics first to verify file is analyzed
|
||||||
|
print("\n4. Checking if document symbols work (to verify analysis)...")
|
||||||
|
symbols = await manager._send_request(
|
||||||
|
state,
|
||||||
|
"textDocument/documentSymbol",
|
||||||
|
{"textDocument": {"uri": test_file.resolve().as_uri()}}
|
||||||
|
)
|
||||||
|
if symbols:
|
||||||
|
print(f" [OK] Found {len(symbols)} symbols:")
|
||||||
|
for s in symbols:
|
||||||
|
name = s.get('name', 'unknown')
|
||||||
|
kind = s.get('kind', 0)
|
||||||
|
range_info = s.get('range', {}).get('start', {})
|
||||||
|
line = range_info.get('line', 0) + 1
|
||||||
|
print(f" - {name} (kind={kind}) at line {line}")
|
||||||
|
else:
|
||||||
|
print(" [WARN] No symbols found!")
|
||||||
|
|
||||||
|
# Now try call hierarchy on different lines
|
||||||
|
print("\n5. Testing prepareCallHierarchy on each symbol...")
|
||||||
|
if symbols:
|
||||||
|
for s in symbols:
|
||||||
|
name = s.get('name', 'unknown')
|
||||||
|
range_info = s.get('range', {}).get('start', {})
|
||||||
|
line = range_info.get('line', 0)
|
||||||
|
char = range_info.get('character', 0)
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"textDocument": {"uri": test_file.resolve().as_uri()},
|
||||||
|
"position": {"line": line, "character": char + 4} # offset into name
|
||||||
|
}
|
||||||
|
|
||||||
|
result = await manager._send_request(
|
||||||
|
state,
|
||||||
|
"textDocument/prepareCallHierarchy",
|
||||||
|
params
|
||||||
|
)
|
||||||
|
|
||||||
|
status = f"[OK] {len(result)} items" if result else "[NONE]"
|
||||||
|
print(f" {name} (line {line+1}, char {char+4}): {status}")
|
||||||
|
if result:
|
||||||
|
for item in result:
|
||||||
|
print(f" - {item.get('name')}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n[ERROR] {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
print("\n6. Cleanup...")
|
||||||
|
await manager.stop()
|
||||||
|
print(" [OK]")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(test_with_wait())
|
||||||
@@ -987,28 +987,35 @@ class StandaloneLspManager:
|
|||||||
file_path: str,
|
file_path: str,
|
||||||
line: int,
|
line: int,
|
||||||
character: int,
|
character: int,
|
||||||
|
wait_for_analysis: float = 2.0,
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""Prepare call hierarchy items for a position.
|
"""Prepare call hierarchy items for a position.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
file_path: Path to the source file
|
file_path: Path to the source file
|
||||||
line: Line number (1-indexed)
|
line: Line number (1-indexed)
|
||||||
character: Character position (1-indexed)
|
character: Character position (1-indexed)
|
||||||
|
wait_for_analysis: Time to wait for server analysis (seconds)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List of CallHierarchyItem dicts
|
List of CallHierarchyItem dicts
|
||||||
"""
|
"""
|
||||||
state = await self._get_server(file_path)
|
state = await self._get_server(file_path)
|
||||||
if not state:
|
if not state:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Check if call hierarchy is supported
|
# Check if call hierarchy is supported
|
||||||
if not state.capabilities.get("callHierarchyProvider"):
|
if not state.capabilities.get("callHierarchyProvider"):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Open document first
|
# Open document first
|
||||||
await self._open_document(state, file_path)
|
await self._open_document(state, file_path)
|
||||||
|
|
||||||
|
# Wait for language server to complete analysis
|
||||||
|
# This is critical for Pyright to return valid call hierarchy items
|
||||||
|
if wait_for_analysis > 0:
|
||||||
|
await asyncio.sleep(wait_for_analysis)
|
||||||
|
|
||||||
result = await self._send_request(
|
result = await self._send_request(
|
||||||
state,
|
state,
|
||||||
"textDocument/prepareCallHierarchy",
|
"textDocument/prepareCallHierarchy",
|
||||||
@@ -1017,10 +1024,10 @@ class StandaloneLspManager:
|
|||||||
"position": self._to_position(line, character),
|
"position": self._to_position(line, character),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if not result or not isinstance(result, list):
|
if not result or not isinstance(result, list):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def get_incoming_calls(
|
async def get_incoming_calls(
|
||||||
|
|||||||
@@ -42,16 +42,20 @@ class AssociationTreeBuilder:
|
|||||||
self,
|
self,
|
||||||
lsp_manager: StandaloneLspManager,
|
lsp_manager: StandaloneLspManager,
|
||||||
timeout: float = 5.0,
|
timeout: float = 5.0,
|
||||||
|
analysis_wait: float = 2.0,
|
||||||
):
|
):
|
||||||
"""Initialize AssociationTreeBuilder.
|
"""Initialize AssociationTreeBuilder.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
lsp_manager: StandaloneLspManager instance for LSP communication
|
lsp_manager: StandaloneLspManager instance for LSP communication
|
||||||
timeout: Timeout for individual LSP requests in seconds
|
timeout: Timeout for individual LSP requests in seconds
|
||||||
|
analysis_wait: Time to wait for LSP analysis on first file (seconds)
|
||||||
"""
|
"""
|
||||||
self.lsp_manager = lsp_manager
|
self.lsp_manager = lsp_manager
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
self.analysis_wait = analysis_wait
|
||||||
self.visited: Set[str] = set()
|
self.visited: Set[str] = set()
|
||||||
|
self._analyzed_files: Set[str] = set() # Track files already analyzed
|
||||||
|
|
||||||
async def build_tree(
|
async def build_tree(
|
||||||
self,
|
self,
|
||||||
@@ -78,6 +82,12 @@ class AssociationTreeBuilder:
|
|||||||
tree = CallTree()
|
tree = CallTree()
|
||||||
self.visited.clear()
|
self.visited.clear()
|
||||||
|
|
||||||
|
# Determine wait time - only wait for analysis on first encounter of file
|
||||||
|
wait_time = 0.0
|
||||||
|
if seed_file_path not in self._analyzed_files:
|
||||||
|
wait_time = self.analysis_wait
|
||||||
|
self._analyzed_files.add(seed_file_path)
|
||||||
|
|
||||||
# Get call hierarchy items for the seed position
|
# Get call hierarchy items for the seed position
|
||||||
try:
|
try:
|
||||||
hierarchy_items = await asyncio.wait_for(
|
hierarchy_items = await asyncio.wait_for(
|
||||||
@@ -85,8 +95,9 @@ class AssociationTreeBuilder:
|
|||||||
file_path=seed_file_path,
|
file_path=seed_file_path,
|
||||||
line=seed_line,
|
line=seed_line,
|
||||||
character=seed_character,
|
character=seed_character,
|
||||||
|
wait_for_analysis=wait_time,
|
||||||
),
|
),
|
||||||
timeout=self.timeout,
|
timeout=self.timeout + wait_time,
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class MockLspManager:
|
|||||||
self.outgoing_calls: Dict[str, List[Dict]] = {}
|
self.outgoing_calls: Dict[str, List[Dict]] = {}
|
||||||
|
|
||||||
async def get_call_hierarchy_items(
|
async def get_call_hierarchy_items(
|
||||||
self, file_path: str, line: int, character: int
|
self, file_path: str, line: int, character: int, wait_for_analysis: float = 0.0
|
||||||
) -> List[Dict]:
|
) -> List[Dict]:
|
||||||
"""Mock get_call_hierarchy_items."""
|
"""Mock get_call_hierarchy_items."""
|
||||||
key = f"{file_path}:{line}:{character}"
|
key = f"{file_path}:{line}:{character}"
|
||||||
|
|||||||
Reference in New Issue
Block a user