feat: pylsp 作为工具之一 + AI 写/改 .py 自动触发诊断并注入上下文
- 补装 pylsp 及 lint 后端(pyflakes/pycodestyle/rope/mccabe),真实诊断可用 - code_editor / file_manager 写/改操作后自动调用 LSP 诊断(.py),结果经 get_lsp_context() 静默注入下一轮模型上下文,仅在有问题时的可见提示 - config.json 中 rust 设为 disabled(本机未装,免 ERROR 噪声) - requirements.txt / config.json.example 补充 pylsp 配置说明 - 新增 4 个 _auto_lsp_check 钩子单测
This commit is contained in:
1 parent
c655d61af4
commit
1054932921
5 files changed
+183
-39
No files matched your search
+35
-7
@@ -599,6 +599,34 @@ else:
|
||||
else:
|
||||
return f"\n{result}"
|
||||
|
||||
def _auto_lsp_check(self, filepath: str) -> str:
|
||||
"""写/改文件后自动让已连接的语言服务器(pylsp 等)重新诊断该文件。
|
||||
|
||||
诊断结果会被缓存进 LSP 管理器的 _workspace_diag,由 get_lsp_context()
|
||||
在下一轮静默注入模型上下文;同时仅在确有问题时给一行可见提示。
|
||||
无对应 server / 文件干净时静默跳过,绝不影响主流程。
|
||||
"""
|
||||
manager = None
|
||||
try:
|
||||
cli = self.cli
|
||||
utm = getattr(cli, 'liugin_manager', None)
|
||||
if utm is not None:
|
||||
manager = getattr(utm, '_lsp_manager', None)
|
||||
except Exception:
|
||||
manager = None
|
||||
if manager is None:
|
||||
return ""
|
||||
try:
|
||||
diag_text = manager.diagnostics(filepath, wait=1.5)
|
||||
except Exception:
|
||||
return ""
|
||||
if not diag_text or "没有为" in diag_text:
|
||||
return "" # 该扩展名未配置 LSP server,跳过
|
||||
if "无诊断" in diag_text:
|
||||
return "" # 干净,无需呈现(缓存也为空)
|
||||
first = diag_text.splitlines()[0] if diag_text else ""
|
||||
return f"\n LSP 诊断: {first}"
|
||||
|
||||
# ══════════════════════════════════════
|
||||
# Diff 弹窗系统
|
||||
# ══════════════════════════════════════
|
||||
@@ -975,7 +1003,7 @@ else:
|
||||
|
||||
self._spawn_diff_popup(fp, before_content, new_content)
|
||||
result = f" 已编辑: {fp}\n\n{self._make_diff(old_text, new_text)}"
|
||||
result += self._auto_syntax_check(fp)
|
||||
result += self._auto_syntax_check(fp) + self._auto_lsp_check(fp)
|
||||
return result
|
||||
|
||||
def _op_multi_edit(self, path: str, rest: str) -> str:
|
||||
@@ -1013,7 +1041,7 @@ else:
|
||||
f.write(content)
|
||||
self._spawn_diff_popup(fp, before_content, content)
|
||||
result = f" 批量编辑: {fp}\n" + "\n".join(results)
|
||||
result += self._auto_syntax_check(fp)
|
||||
result += self._auto_syntax_check(fp) + self._auto_lsp_check(fp)
|
||||
return result
|
||||
|
||||
def _op_insert(self, path: str, rest: str) -> str:
|
||||
@@ -1046,7 +1074,7 @@ else:
|
||||
after_content = ''.join(lines)
|
||||
self._spawn_diff_popup(fp, before_content, after_content)
|
||||
result = f" 已在第 {line_num} 行插入 {len(insert_lines)} 行"
|
||||
result += self._auto_syntax_check(fp)
|
||||
result += self._auto_syntax_check(fp) + self._auto_lsp_check(fp)
|
||||
return result
|
||||
|
||||
def _op_delete_lines(self, path: str, rest: str) -> str:
|
||||
@@ -1079,7 +1107,7 @@ else:
|
||||
after_content = ''.join(lines)
|
||||
self._spawn_diff_popup(fp, before_content, after_content)
|
||||
result = f" 已删除第 {start}-{end} 行 ({len(deleted)} 行)"
|
||||
result += self._auto_syntax_check(fp)
|
||||
result += self._auto_syntax_check(fp) + self._auto_lsp_check(fp)
|
||||
return result
|
||||
|
||||
def _op_create(self, path: str, rest: str) -> str:
|
||||
@@ -1095,7 +1123,7 @@ else:
|
||||
f.write(rest or "")
|
||||
self._spawn_diff_popup(fp, "", rest or "")
|
||||
result = f" 已创建: {fp} ({len(rest or '')} 字符)"
|
||||
result += self._auto_syntax_check(fp)
|
||||
result += self._auto_syntax_check(fp) + self._auto_lsp_check(fp)
|
||||
return result
|
||||
|
||||
def _op_write(self, path: str, rest: str) -> str:
|
||||
@@ -1110,7 +1138,7 @@ else:
|
||||
f.write(rest)
|
||||
self._spawn_diff_popup(fp, before_content, rest)
|
||||
result = f" 已写入: {fp} ({len(rest)} 字符)"
|
||||
result += self._auto_syntax_check(fp)
|
||||
result += self._auto_syntax_check(fp) + self._auto_lsp_check(fp)
|
||||
return result
|
||||
|
||||
def _op_append(self, path: str, rest: str) -> str:
|
||||
@@ -1125,7 +1153,7 @@ else:
|
||||
after_content = self._read_file_safe(fp)
|
||||
self._spawn_diff_popup(fp, before_content, after_content)
|
||||
result = f" 已追加: {fp} ({len(rest)} 字符)"
|
||||
result += self._auto_syntax_check(fp)
|
||||
result += self._auto_syntax_check(fp) + self._auto_lsp_check(fp)
|
||||
return result
|
||||
|
||||
# ══════════════════════════════════════
|
||||
|
||||
+38
-2
@@ -38,6 +38,34 @@ class Liugin:
|
||||
def set_cli(self, cli):
|
||||
self.cli = cli
|
||||
|
||||
def _auto_lsp_check(self, filepath: str) -> str:
|
||||
"""写/改文件后自动让已连接的语言服务器(pylsp 等)重新诊断该文件。
|
||||
|
||||
诊断结果缓存进 LSP 管理器的 _workspace_diag,由 get_lsp_context()
|
||||
在下一轮静默注入模型上下文;仅在有问题时返回一行提示。
|
||||
无对应 server / 文件干净时静默跳过。
|
||||
"""
|
||||
manager = None
|
||||
try:
|
||||
cli = self.cli
|
||||
utm = getattr(cli, 'liugin_manager', None)
|
||||
if utm is not None:
|
||||
manager = getattr(utm, '_lsp_manager', None)
|
||||
except Exception:
|
||||
manager = None
|
||||
if manager is None:
|
||||
return ""
|
||||
try:
|
||||
diag_text = manager.diagnostics(filepath, wait=1.5)
|
||||
except Exception:
|
||||
return ""
|
||||
if not diag_text or "没有为" in diag_text:
|
||||
return ""
|
||||
if "无诊断" in diag_text:
|
||||
return ""
|
||||
first = diag_text.splitlines()[0] if diag_text else ""
|
||||
return f"\n LSP 诊断: {first}"
|
||||
|
||||
def get_tool_info(self):
|
||||
return {
|
||||
"name": "file_manager",
|
||||
@@ -219,7 +247,11 @@ class Liugin:
|
||||
os.makedirs(os.path.dirname(fp) or ".", exist_ok=True)
|
||||
with open(fp, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
return ToolResult.ok(f"已写入: {fp} ({len(content)} 字符)", tool_name="file_manager")
|
||||
msg = f"已写入: {fp} ({len(content)} 字符)"
|
||||
lsp_hint = self._auto_lsp_check(fp)
|
||||
if lsp_hint:
|
||||
msg += lsp_hint
|
||||
return ToolResult.ok(msg, tool_name="file_manager")
|
||||
|
||||
def _op_append(self, args: List[str]):
|
||||
if len(args) < 2:
|
||||
@@ -237,7 +269,11 @@ class Liugin:
|
||||
return ToolResult.not_found(f"文件: {fp}")
|
||||
with open(fp, 'a', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
return ToolResult.ok(f"已追加: {fp}", tool_name="file_manager")
|
||||
msg = f"已追加: {fp}"
|
||||
lsp_hint = self._auto_lsp_check(fp)
|
||||
if lsp_hint:
|
||||
msg += lsp_hint
|
||||
return ToolResult.ok(msg, tool_name="file_manager")
|
||||
|
||||
def _op_copy(self, args: List[str]):
|
||||
if len(args) < 2:
|
||||
|
||||
Reference in new issue
Block a user