feat: 自研本地 LSP 服务器 (jedi 语义 + py_detect 诊断),替代 pylsp

- xcli_core/local_lsp_server.py: LSP over stdio (Content-Length 分帧),兼容现有 lsp_client
  - 语义: jedi 0.20 API (goto/infer/get_references/complete/help) — 补全/跳转/引用/悬停
  - 诊断: 复用 plugins/py_detect 规则引擎,中文诊断 + 修复建议,didOpen/didChange 自动推送
  - 零网络、零外部安装 (无需 pip install pylsp),服务器无响应时客户端可超时跳过
- config.json(.example): python 语言默认切到本地服务器; 换回 pylsp 改 command 即可
- lsp_client.py: command 为裸 python 且 PATH 找不到时回退 sys.executable (防环境坑)
- 16 项测试 (分帧/路径/协议/语义/真实 subprocess 完整握手); 相关套件 277 passed 零回归
This commit is contained in:
shuiyu67 committed 2026-08-07 02:00:49 +08:00
1 parent 9a133dcf88
commit 56d5ed27e0
4 files changed
+675 -3

No files matched your search

+10
View File
@@ -18,6 +18,8 @@ import os
import re
import json
import time
import shutil
import sys
import threading
import subprocess
import logging
@@ -115,6 +117,14 @@ class LspClient:
full_env = dict(os.environ)
full_env.update(self.env)
cmd = [self.command] + self.args
# 鲁棒性: command 为裸 "python"/"python3" 但 PATH 找不到时, 回退当前解释器
if cmd and os.path.basename(cmd[0]) in ("python", "python3") and os.path.dirname(cmd[0]) == "":
found = None
for cand in (cmd[0], cmd[0] + ".exe"):
if found is None and shutil.which(cand):
found = cand
if not found:
cmd = [sys.executable] + self.args
try:
# 二进制模式:避免 CJK 字符数与 Content-Length 字节数不一致
self.proc = subprocess.Popen(