mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-11 02:33:51 +08:00
Add TypeScript LSP setup guide and enhance debugging tests
- Created a comprehensive guide for setting up TypeScript LSP in Claude Code, detailing installation methods, configuration, and troubleshooting. - Added multiple debugging test scripts to validate LSP communication with pyright, including direct communication tests, configuration checks, and document symbol retrieval. - Implemented error handling and logging for better visibility during LSP interactions.
This commit is contained in:
149
codex-lens/tests/real/debug_compare.py
Normal file
149
codex-lens/tests/real/debug_compare.py
Normal file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python
|
||||
"""Compare manager read behavior vs direct read."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
|
||||
|
||||
from codexlens.lsp.standalone_manager import StandaloneLspManager
|
||||
|
||||
|
||||
async def direct_test():
|
||||
"""Direct communication - this works."""
|
||||
workspace = Path(__file__).parent.parent.parent
|
||||
print("\n=== DIRECT TEST ===")
|
||||
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
"pyright-langserver", "--stdio",
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd=str(workspace),
|
||||
)
|
||||
|
||||
def encode_message(content):
|
||||
body = json.dumps(content).encode("utf-8")
|
||||
header = f"Content-Length: {len(body)}\r\n\r\n"
|
||||
return header.encode("ascii") + body
|
||||
|
||||
async def send(message):
|
||||
encoded = encode_message(message)
|
||||
process.stdin.write(encoded)
|
||||
await process.stdin.drain()
|
||||
msg_desc = message.get('method') or f"response id={message.get('id')}"
|
||||
print(f" SENT: {msg_desc}")
|
||||
|
||||
async def read_one():
|
||||
content_length = 0
|
||||
while True:
|
||||
line = await asyncio.wait_for(process.stdout.readline(), timeout=3.0)
|
||||
if not line:
|
||||
return None
|
||||
line_str = line.decode("ascii").strip()
|
||||
if not line_str:
|
||||
break
|
||||
if line_str.lower().startswith("content-length:"):
|
||||
content_length = int(line_str.split(":")[1].strip())
|
||||
if content_length == 0:
|
||||
return None
|
||||
body = await process.stdout.readexactly(content_length)
|
||||
return json.loads(body.decode("utf-8"))
|
||||
|
||||
# Initialize
|
||||
print(" Sending initialize...")
|
||||
await send({
|
||||
"jsonrpc": "2.0", "id": 1, "method": "initialize",
|
||||
"params": {
|
||||
"processId": None,
|
||||
"rootUri": workspace.as_uri(),
|
||||
"capabilities": {"workspace": {"configuration": True}},
|
||||
"workspaceFolders": [{"uri": workspace.as_uri(), "name": workspace.name}],
|
||||
},
|
||||
})
|
||||
|
||||
# Read until response
|
||||
while True:
|
||||
msg = await read_one()
|
||||
if msg and msg.get("id") == 1:
|
||||
print(f" Initialize response OK")
|
||||
break
|
||||
elif msg:
|
||||
print(f" Notification: {msg.get('method')}")
|
||||
|
||||
# Send initialized
|
||||
print(" Sending initialized...")
|
||||
await send({"jsonrpc": "2.0", "method": "initialized", "params": {}})
|
||||
|
||||
# Check for workspace/configuration
|
||||
print(" Checking for workspace/configuration (3s timeout)...")
|
||||
try:
|
||||
for i in range(10):
|
||||
msg = await read_one()
|
||||
if msg:
|
||||
method = msg.get("method")
|
||||
msg_id = msg.get("id")
|
||||
print(f" RECV: {method or 'response'} (id={msg_id})")
|
||||
if method == "workspace/configuration":
|
||||
print(" SUCCESS: workspace/configuration received!")
|
||||
break
|
||||
except asyncio.TimeoutError:
|
||||
print(" TIMEOUT: No more messages")
|
||||
|
||||
process.terminate()
|
||||
await process.wait()
|
||||
|
||||
|
||||
async def manager_test():
|
||||
"""Manager communication - investigating why this doesn't work."""
|
||||
workspace = Path(__file__).parent.parent.parent
|
||||
print("\n=== MANAGER TEST ===")
|
||||
|
||||
manager = StandaloneLspManager(
|
||||
workspace_root=str(workspace),
|
||||
timeout=60.0
|
||||
)
|
||||
await manager.start()
|
||||
|
||||
# Just check if server initialized
|
||||
state = manager._servers.get("python")
|
||||
if state:
|
||||
print(f" Server initialized: {state.initialized}")
|
||||
print(f" Capabilities: {len(state.capabilities)} keys")
|
||||
else:
|
||||
# Force initialization by getting server for a Python file
|
||||
print(" Getting server for Python file...")
|
||||
test_file = workspace / "tests" / "real" / "debug_compare.py"
|
||||
state = await manager._get_server(str(test_file))
|
||||
if state:
|
||||
print(f" Server initialized: {state.initialized}")
|
||||
|
||||
# Try to read directly from state.reader
|
||||
if state:
|
||||
print("\n Direct read test from state.reader:")
|
||||
print(f" state.reader is: {type(state.reader)}")
|
||||
print(f" state.reader at_eof: {state.reader.at_eof()}")
|
||||
|
||||
# Check if there's data available
|
||||
try:
|
||||
line = await asyncio.wait_for(state.reader.readline(), timeout=1.0)
|
||||
if line:
|
||||
print(f" Got line: {line[:50]}...")
|
||||
else:
|
||||
print(f" readline returned empty (EOF)")
|
||||
except asyncio.TimeoutError:
|
||||
print(f" readline timed out (no data)")
|
||||
|
||||
await manager.stop()
|
||||
|
||||
|
||||
async def main():
|
||||
await direct_test()
|
||||
await manager_test()
|
||||
print("\n=== DONE ===")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
216
codex-lens/tests/real/debug_config.py
Normal file
216
codex-lens/tests/real/debug_config.py
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python
|
||||
"""Test if pyright sends workspace/configuration after initialized."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add source to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
|
||||
|
||||
|
||||
async def read_message_direct(reader):
|
||||
"""Read a JSON-RPC message - direct blocking read, no timeout."""
|
||||
content_length = 0
|
||||
while True:
|
||||
line = await reader.readline()
|
||||
if not line:
|
||||
return None
|
||||
line_str = line.decode("ascii").strip()
|
||||
if not line_str:
|
||||
break
|
||||
if line_str.lower().startswith("content-length:"):
|
||||
content_length = int(line_str.split(":")[1].strip())
|
||||
|
||||
if content_length == 0:
|
||||
return None
|
||||
|
||||
body = await reader.readexactly(content_length)
|
||||
return json.loads(body.decode("utf-8"))
|
||||
|
||||
|
||||
async def main():
|
||||
workspace = Path(__file__).parent.parent.parent
|
||||
print(f"Workspace: {workspace}")
|
||||
|
||||
# Start pyright - exactly like in direct test
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
"pyright-langserver", "--stdio",
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd=str(workspace),
|
||||
)
|
||||
|
||||
def encode_message(content):
|
||||
body = json.dumps(content).encode("utf-8")
|
||||
header = f"Content-Length: {len(body)}\r\n\r\n"
|
||||
return header.encode("ascii") + body
|
||||
|
||||
async def send(message):
|
||||
encoded = encode_message(message)
|
||||
process.stdin.write(encoded)
|
||||
await process.stdin.drain()
|
||||
method_or_resp = message.get('method') or f"response id={message.get('id')}"
|
||||
print(f"SENT: {method_or_resp} ({len(encoded)} bytes)")
|
||||
|
||||
# Start stderr reader
|
||||
async def read_stderr():
|
||||
while True:
|
||||
line = await process.stderr.readline()
|
||||
if not line:
|
||||
break
|
||||
print(f"[stderr] {line.decode('utf-8', errors='replace').rstrip()}")
|
||||
asyncio.create_task(read_stderr())
|
||||
|
||||
print("\n=== INITIALIZE ===")
|
||||
await send({
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"processId": None,
|
||||
"rootUri": workspace.as_uri(),
|
||||
"rootPath": str(workspace),
|
||||
"capabilities": {
|
||||
"workspace": {"configuration": True},
|
||||
},
|
||||
"workspaceFolders": [{"uri": workspace.as_uri(), "name": workspace.name}],
|
||||
},
|
||||
})
|
||||
|
||||
# Read until we get initialize response
|
||||
print("Reading initialize response...")
|
||||
while True:
|
||||
msg = await asyncio.wait_for(read_message_direct(process.stdout), timeout=10)
|
||||
if msg is None:
|
||||
break
|
||||
method = msg.get("method")
|
||||
msg_id = msg.get("id")
|
||||
if method:
|
||||
print(f"RECV: {method} (notification)")
|
||||
else:
|
||||
print(f"RECV: response id={msg_id}")
|
||||
if msg_id == 1:
|
||||
print("Initialize OK!")
|
||||
break
|
||||
|
||||
print("\n=== SEND INITIALIZED ===")
|
||||
await send({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "initialized",
|
||||
"params": {},
|
||||
})
|
||||
|
||||
# Now, here's the key test - will we receive workspace/configuration?
|
||||
print("\n=== WAIT FOR workspace/configuration ===")
|
||||
print("Reading with 5 second timeout...")
|
||||
|
||||
try:
|
||||
for i in range(10):
|
||||
msg = await asyncio.wait_for(read_message_direct(process.stdout), timeout=2)
|
||||
if msg is None:
|
||||
print("EOF")
|
||||
break
|
||||
method = msg.get("method")
|
||||
msg_id = msg.get("id")
|
||||
print(f"RECV: method={method}, id={msg_id}")
|
||||
|
||||
# Respond to server requests
|
||||
if msg_id is not None and method:
|
||||
if method == "workspace/configuration":
|
||||
print(" -> Got workspace/configuration! Responding...")
|
||||
await send({
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": [{} for _ in msg.get("params", {}).get("items", [])],
|
||||
})
|
||||
else:
|
||||
print(f" -> Responding to {method}")
|
||||
await send({"jsonrpc": "2.0", "id": msg_id, "result": None})
|
||||
except asyncio.TimeoutError:
|
||||
print("No more messages (timeout)")
|
||||
|
||||
print("\n=== Now start background read task like manager does ===")
|
||||
|
||||
# Store references like manager does
|
||||
reader = process.stdout # This is how manager does it
|
||||
writer = process.stdin
|
||||
|
||||
# Start background read task
|
||||
async def bg_read_loop():
|
||||
print("[BG] Read loop started")
|
||||
try:
|
||||
while True:
|
||||
await asyncio.sleep(0)
|
||||
try:
|
||||
msg = await asyncio.wait_for(read_message_direct(reader), timeout=1.0)
|
||||
if msg is None:
|
||||
print("[BG] Stream closed")
|
||||
break
|
||||
bg_method = msg.get('method') or f"response id={msg.get('id')}"
|
||||
print(f"[BG] RECV: {bg_method}")
|
||||
|
||||
# Handle server requests
|
||||
method = msg.get("method")
|
||||
msg_id = msg.get("id")
|
||||
if msg_id is not None and method:
|
||||
print(f"[BG] Responding to {method}")
|
||||
await send({"jsonrpc": "2.0", "id": msg_id, "result": None})
|
||||
except asyncio.TimeoutError:
|
||||
print("[BG] timeout")
|
||||
except asyncio.CancelledError:
|
||||
print("[BG] Cancelled")
|
||||
|
||||
read_task = asyncio.create_task(bg_read_loop())
|
||||
|
||||
# Wait a moment
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
# Now send didOpen and documentSymbol like manager does
|
||||
print("\n=== SEND didOpen ===")
|
||||
test_file = workspace / "tests" / "real" / "debug_config.py"
|
||||
await send({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "textDocument/didOpen",
|
||||
"params": {
|
||||
"textDocument": {
|
||||
"uri": test_file.as_uri(),
|
||||
"languageId": "python",
|
||||
"version": 1,
|
||||
"text": test_file.read_text(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
# Wait for processing
|
||||
await asyncio.sleep(2)
|
||||
|
||||
print("\n=== SEND documentSymbol ===")
|
||||
await send({
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "textDocument/documentSymbol",
|
||||
"params": {"textDocument": {"uri": test_file.as_uri()}},
|
||||
})
|
||||
|
||||
# Wait for response
|
||||
print("Waiting for documentSymbol response (max 30s)...")
|
||||
deadline = asyncio.get_event_loop().time() + 30
|
||||
while asyncio.get_event_loop().time() < deadline:
|
||||
await asyncio.sleep(0.5)
|
||||
# The background task will print when it receives the response
|
||||
|
||||
read_task.cancel()
|
||||
try:
|
||||
await read_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
process.terminate()
|
||||
print("\nDone!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
320
codex-lens/tests/real/debug_direct.py
Normal file
320
codex-lens/tests/real/debug_direct.py
Normal file
@@ -0,0 +1,320 @@
|
||||
#!/usr/bin/env python
|
||||
"""Minimal direct test of pyright LSP communication."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
async def send_message(writer, message):
|
||||
"""Send a JSON-RPC message."""
|
||||
body = json.dumps(message).encode("utf-8")
|
||||
header = f"Content-Length: {len(body)}\r\n\r\n".encode("ascii")
|
||||
writer.write(header + body)
|
||||
await writer.drain()
|
||||
print(f"SENT: {message.get('method', 'response')} (id={message.get('id', 'N/A')})")
|
||||
|
||||
|
||||
async def read_message(reader):
|
||||
"""Read a JSON-RPC message."""
|
||||
# Read headers
|
||||
content_length = 0
|
||||
while True:
|
||||
line = await reader.readline()
|
||||
if not line:
|
||||
return None
|
||||
line_str = line.decode("ascii").strip()
|
||||
if not line_str:
|
||||
break
|
||||
if line_str.lower().startswith("content-length:"):
|
||||
content_length = int(line_str.split(":")[1].strip())
|
||||
|
||||
if content_length == 0:
|
||||
return None
|
||||
|
||||
# Read body
|
||||
body = await reader.readexactly(content_length)
|
||||
return json.loads(body.decode("utf-8"))
|
||||
|
||||
|
||||
async def main():
|
||||
workspace = Path(__file__).parent.parent.parent
|
||||
test_file = workspace / "tests" / "real" / "debug_direct.py"
|
||||
|
||||
print(f"Workspace: {workspace}")
|
||||
print(f"Test file: {test_file}")
|
||||
print()
|
||||
|
||||
# Start pyright
|
||||
print("Starting pyright-langserver...")
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
"pyright-langserver", "--stdio",
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd=str(workspace),
|
||||
)
|
||||
|
||||
# Start stderr reader
|
||||
async def read_stderr():
|
||||
while True:
|
||||
line = await process.stderr.readline()
|
||||
if not line:
|
||||
break
|
||||
print(f"[stderr] {line.decode('utf-8', errors='replace').rstrip()}")
|
||||
|
||||
stderr_task = asyncio.create_task(read_stderr())
|
||||
|
||||
try:
|
||||
# 1. Send initialize
|
||||
print("\n=== INITIALIZE ===")
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"processId": None,
|
||||
"rootUri": workspace.as_uri(),
|
||||
"rootPath": str(workspace),
|
||||
"capabilities": {
|
||||
"textDocument": {
|
||||
"documentSymbol": {
|
||||
"hierarchicalDocumentSymbolSupport": True,
|
||||
},
|
||||
},
|
||||
"workspace": {
|
||||
"configuration": True,
|
||||
},
|
||||
},
|
||||
"workspaceFolders": [{"uri": workspace.as_uri(), "name": workspace.name}],
|
||||
},
|
||||
})
|
||||
|
||||
# Read all messages until we get initialize response
|
||||
print("\n=== READING RESPONSES ===")
|
||||
init_done = False
|
||||
for i in range(20):
|
||||
try:
|
||||
msg = await asyncio.wait_for(read_message(process.stdout), timeout=5.0)
|
||||
if msg is None:
|
||||
print("EOF")
|
||||
break
|
||||
|
||||
method = msg.get("method", "")
|
||||
msg_id = msg.get("id", "N/A")
|
||||
|
||||
if method:
|
||||
print(f"RECV: {method} (id={msg_id})")
|
||||
|
||||
# Handle server requests
|
||||
if msg_id != "N/A":
|
||||
if method == "workspace/configuration":
|
||||
print(" -> Responding to workspace/configuration")
|
||||
items = msg.get("params", {}).get("items", [])
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": [{"pythonPath": "python"} for _ in items],
|
||||
})
|
||||
elif method == "client/registerCapability":
|
||||
print(" -> Responding to client/registerCapability")
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": None,
|
||||
})
|
||||
elif method == "window/workDoneProgress/create":
|
||||
print(" -> Responding to window/workDoneProgress/create")
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": None,
|
||||
})
|
||||
else:
|
||||
print(f"RECV: response (id={msg_id})")
|
||||
if msg_id == 1:
|
||||
print(" -> Initialize response received!")
|
||||
caps = list(msg.get("result", {}).get("capabilities", {}).keys())
|
||||
print(f" -> Capabilities: {caps[:5]}...")
|
||||
init_done = True
|
||||
break
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
print(f" Timeout waiting for message {i+1}")
|
||||
break
|
||||
|
||||
if not init_done:
|
||||
print("ERROR: Initialize failed")
|
||||
return
|
||||
|
||||
# 2. Send initialized notification
|
||||
print("\n=== INITIALIZED ===")
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "initialized",
|
||||
"params": {},
|
||||
})
|
||||
|
||||
# Read any messages pyright sends after initialized
|
||||
print("\n=== READING POST-INITIALIZED MESSAGES ===")
|
||||
for i in range(10):
|
||||
try:
|
||||
msg = await asyncio.wait_for(read_message(process.stdout), timeout=2.0)
|
||||
if msg is None:
|
||||
break
|
||||
|
||||
method = msg.get("method", "")
|
||||
msg_id = msg.get("id", "N/A")
|
||||
|
||||
print(f"RECV: {method or 'response'} (id={msg_id})")
|
||||
|
||||
# Handle server requests
|
||||
if msg_id != "N/A" and method:
|
||||
if method == "workspace/configuration":
|
||||
print(" -> Responding to workspace/configuration")
|
||||
items = msg.get("params", {}).get("items", [])
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": [{"pythonPath": "python"} for _ in items],
|
||||
})
|
||||
elif method == "client/registerCapability":
|
||||
print(" -> Responding to client/registerCapability")
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": None,
|
||||
})
|
||||
elif method == "window/workDoneProgress/create":
|
||||
print(" -> Responding to window/workDoneProgress/create")
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": None,
|
||||
})
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
print(f" No more messages (timeout)")
|
||||
break
|
||||
|
||||
# 3. Send didOpen
|
||||
print("\n=== DIDOPEN ===")
|
||||
content = test_file.read_text(encoding="utf-8")
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "textDocument/didOpen",
|
||||
"params": {
|
||||
"textDocument": {
|
||||
"uri": test_file.as_uri(),
|
||||
"languageId": "python",
|
||||
"version": 1,
|
||||
"text": content,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
# Read any messages
|
||||
print("\n=== READING POST-DIDOPEN MESSAGES ===")
|
||||
for i in range(10):
|
||||
try:
|
||||
msg = await asyncio.wait_for(read_message(process.stdout), timeout=2.0)
|
||||
if msg is None:
|
||||
break
|
||||
|
||||
method = msg.get("method", "")
|
||||
msg_id = msg.get("id", "N/A")
|
||||
|
||||
print(f"RECV: {method or 'response'} (id={msg_id})")
|
||||
|
||||
# Handle server requests
|
||||
if msg_id != "N/A" and method:
|
||||
if method == "workspace/configuration":
|
||||
print(" -> Responding to workspace/configuration")
|
||||
items = msg.get("params", {}).get("items", [])
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": [{"pythonPath": "python"} for _ in items],
|
||||
})
|
||||
else:
|
||||
print(f" -> Responding with null to {method}")
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": None,
|
||||
})
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
print(f" No more messages (timeout)")
|
||||
break
|
||||
|
||||
# 4. Send documentSymbol request
|
||||
print("\n=== DOCUMENTSYMBOL ===")
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "textDocument/documentSymbol",
|
||||
"params": {
|
||||
"textDocument": {"uri": test_file.as_uri()},
|
||||
},
|
||||
})
|
||||
|
||||
# Wait for response
|
||||
print("\n=== READING DOCUMENTSYMBOL RESPONSE ===")
|
||||
for i in range(20):
|
||||
try:
|
||||
msg = await asyncio.wait_for(read_message(process.stdout), timeout=5.0)
|
||||
if msg is None:
|
||||
break
|
||||
|
||||
method = msg.get("method", "")
|
||||
msg_id = msg.get("id", "N/A")
|
||||
|
||||
if method:
|
||||
print(f"RECV: {method} (id={msg_id})")
|
||||
|
||||
# Handle server requests
|
||||
if msg_id != "N/A":
|
||||
if method == "workspace/configuration":
|
||||
print(" -> Responding to workspace/configuration")
|
||||
items = msg.get("params", {}).get("items", [])
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": [{"pythonPath": "python"} for _ in items],
|
||||
})
|
||||
else:
|
||||
print(f" -> Responding with null to {method}")
|
||||
await send_message(process.stdin, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": msg_id,
|
||||
"result": None,
|
||||
})
|
||||
else:
|
||||
print(f"RECV: response (id={msg_id})")
|
||||
if msg_id == 2:
|
||||
result = msg.get("result", [])
|
||||
print(f" -> DocumentSymbol response: {len(result)} symbols")
|
||||
for sym in result[:5]:
|
||||
print(f" - {sym.get('name')} ({sym.get('kind')})")
|
||||
break
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
print(f" Timeout {i+1}")
|
||||
if i >= 5:
|
||||
break
|
||||
|
||||
print("\n=== DONE ===")
|
||||
|
||||
finally:
|
||||
stderr_task.cancel()
|
||||
process.terminate()
|
||||
try:
|
||||
await asyncio.wait_for(process.wait(), timeout=5.0)
|
||||
except asyncio.TimeoutError:
|
||||
process.kill()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
123
codex-lens/tests/real/debug_reads.py
Normal file
123
codex-lens/tests/real/debug_reads.py
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python
|
||||
"""Debug exactly what's happening with reads after initialized."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
async def main():
|
||||
workspace = Path(__file__).parent.parent.parent
|
||||
print(f"Workspace: {workspace}")
|
||||
|
||||
# Start pyright
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
"pyright-langserver", "--stdio",
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd=str(workspace),
|
||||
)
|
||||
|
||||
# Helper to encode message
|
||||
def encode(content):
|
||||
body = json.dumps(content).encode("utf-8")
|
||||
header = f"Content-Length: {len(body)}\r\n\r\n"
|
||||
return header.encode("ascii") + body
|
||||
|
||||
# Helper to send
|
||||
async def send(msg):
|
||||
encoded = encode(msg)
|
||||
process.stdin.write(encoded)
|
||||
await process.stdin.drain()
|
||||
method = msg.get("method") or f"response-{msg.get('id')}"
|
||||
print(f"SENT: {method}")
|
||||
|
||||
# Helper to read one message
|
||||
async def read_one(timeout=3.0):
|
||||
content_length = 0
|
||||
while True:
|
||||
try:
|
||||
print(f" readline(timeout={timeout})...")
|
||||
line = await asyncio.wait_for(process.stdout.readline(), timeout=timeout)
|
||||
print(f" got line: {repr(line[:50] if len(line) > 50 else line)}")
|
||||
except asyncio.TimeoutError:
|
||||
print(f" TIMEOUT on readline")
|
||||
return None
|
||||
|
||||
if not line:
|
||||
print(f" EOF")
|
||||
return None
|
||||
|
||||
line_str = line.decode("ascii").strip()
|
||||
if not line_str:
|
||||
break # End of headers
|
||||
|
||||
if line_str.lower().startswith("content-length:"):
|
||||
content_length = int(line_str.split(":")[1].strip())
|
||||
|
||||
if content_length == 0:
|
||||
return None
|
||||
|
||||
body = await process.stdout.readexactly(content_length)
|
||||
return json.loads(body.decode("utf-8"))
|
||||
|
||||
# Start stderr reader
|
||||
async def read_stderr():
|
||||
while True:
|
||||
line = await process.stderr.readline()
|
||||
if not line:
|
||||
break
|
||||
print(f"[stderr] {line.decode('utf-8', errors='replace').rstrip()}")
|
||||
asyncio.create_task(read_stderr())
|
||||
|
||||
print("\n=== INITIALIZE ===")
|
||||
await send({
|
||||
"jsonrpc": "2.0", "id": 1, "method": "initialize",
|
||||
"params": {
|
||||
"processId": None,
|
||||
"rootUri": workspace.as_uri(),
|
||||
"capabilities": {"workspace": {"configuration": True}},
|
||||
"workspaceFolders": [{"uri": workspace.as_uri(), "name": workspace.name}],
|
||||
},
|
||||
})
|
||||
|
||||
# Read until initialize response
|
||||
print("\n=== READING UNTIL INITIALIZE RESPONSE ===")
|
||||
while True:
|
||||
msg = await read_one()
|
||||
if msg and msg.get("id") == 1 and "method" not in msg:
|
||||
print(f"Got initialize response")
|
||||
break
|
||||
elif msg:
|
||||
print(f"Got notification: {msg.get('method')}")
|
||||
|
||||
print("\n=== SEND INITIALIZED ===")
|
||||
await send({"jsonrpc": "2.0", "method": "initialized", "params": {}})
|
||||
|
||||
print("\n=== NOW TRY TO READ WORKSPACE/CONFIGURATION ===")
|
||||
print("Attempting reads with 2s timeout each...")
|
||||
|
||||
for i in range(3):
|
||||
print(f"\n--- Read attempt {i+1} ---")
|
||||
msg = await read_one(timeout=2.0)
|
||||
if msg:
|
||||
method = msg.get("method", "")
|
||||
msg_id = msg.get("id")
|
||||
print(f"SUCCESS: method={method}, id={msg_id}")
|
||||
if method and msg_id is not None:
|
||||
# Respond to server request
|
||||
print(f"Responding to {method}")
|
||||
await send({"jsonrpc": "2.0", "id": msg_id, "result": [{}]})
|
||||
else:
|
||||
print(f"No message (timeout or EOF)")
|
||||
break
|
||||
|
||||
print("\n=== CLEANUP ===")
|
||||
process.terminate()
|
||||
await process.wait()
|
||||
print("Done")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user