feat: diff display mode - popup or inline (SSH compatible)
- Startup prompt asks user to choose popup or inline diff mode - Added inline diff display in main terminal with colored output - Green for added lines, red for deleted lines (both modes) - TUI mode uses Rich markup, CLI mode uses ANSI colors - New commands: diff_popup popup / diff_popup inline - Default: inline mode (SSH compatible)
This commit is contained in:
1 parent
f41d5d5a7c
commit
baa2cbc49a
2 files changed
+180
-28
No files matched your search
+137
-25
@@ -37,9 +37,10 @@ class Plugin:
|
|||||||
编辑操作后自动执行,无需手动调用
|
编辑操作后自动执行,无需手动调用
|
||||||
|
|
||||||
Diff 弹窗:
|
Diff 弹窗:
|
||||||
diff_popup [on|off] - 开关 diff 弹窗 (别名: popup)
|
diff_popup [on|off|popup|inline] - 开关/切换 diff 显示模式 (别名: popup)
|
||||||
编辑操作后弹出新窗口显示修改前后对比(默认开启)
|
popup/弹窗 → 弹窗模式(新终端窗口)
|
||||||
on/开/enable → 开启 | off/关/disable → 关闭 | 无参数 → 切换
|
inline/ssh → 主终端内显示(SSH 兼容)
|
||||||
|
on/开 → 开启 | off/关 → 关闭 | 无参数 → 切换
|
||||||
|
|
||||||
查看操作:
|
查看操作:
|
||||||
read_range <文件> <起始行> [结束行] - 读取指定行范围
|
read_range <文件> <起始行> [结束行] - 读取指定行范围
|
||||||
@@ -59,7 +60,8 @@ Diff 弹窗:
|
|||||||
stats <目录> - 代码统计
|
stats <目录> - 代码统计
|
||||||
"""
|
"""
|
||||||
self.cli = None
|
self.cli = None
|
||||||
self.diff_popup_enabled = True # diff 弹窗开关,默认开启
|
self.diff_popup_enabled = True # diff 弹窗开关
|
||||||
|
self.diff_popup_mode = None # None=启动时询问, True=弹窗, False=主终端内显示
|
||||||
|
|
||||||
def set_cli(self, cli):
|
def set_cli(self, cli):
|
||||||
self.cli = cli
|
self.cli = cli
|
||||||
@@ -611,20 +613,26 @@ else:
|
|||||||
|
|
||||||
def _spawn_diff_popup(self, filepath: str, before: str, after: str):
|
def _spawn_diff_popup(self, filepath: str, before: str, after: str):
|
||||||
"""
|
"""
|
||||||
在新终端窗口中显示修改前后的对比。
|
显示修改前后的对比。
|
||||||
使用独立线程避免阻塞主流程。
|
根据 diff_popup_mode 决定弹窗还是主终端内显示。
|
||||||
"""
|
"""
|
||||||
if not self.diff_popup_enabled:
|
if not self.diff_popup_enabled:
|
||||||
return
|
return
|
||||||
if before == after:
|
if before == after:
|
||||||
return # 内容无变化,不弹窗
|
return # 内容无变化,不弹窗
|
||||||
|
|
||||||
|
# 启动时未选择模式,默认为主终端内显示(SSH 兼容)
|
||||||
|
popup = self.diff_popup_mode if self.diff_popup_mode is not None else False
|
||||||
|
|
||||||
|
if popup:
|
||||||
thread = threading.Thread(
|
thread = threading.Thread(
|
||||||
target=self._do_spawn_diff_popup,
|
target=self._do_spawn_diff_popup,
|
||||||
args=(filepath, before, after),
|
args=(filepath, before, after),
|
||||||
daemon=True
|
daemon=True
|
||||||
)
|
)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
else:
|
||||||
|
self._show_inline_diff(filepath, before, after)
|
||||||
|
|
||||||
def _do_spawn_diff_popup(self, filepath: str, before: str, after: str):
|
def _do_spawn_diff_popup(self, filepath: str, before: str, after: str):
|
||||||
"""实际执行弹窗创建(在子线程中运行)"""
|
"""实际执行弹窗创建(在子线程中运行)"""
|
||||||
@@ -642,7 +650,6 @@ else:
|
|||||||
tofile=f"修改后: {basename}",
|
tofile=f"修改后: {basename}",
|
||||||
lineterm=''
|
lineterm=''
|
||||||
))
|
))
|
||||||
diff_text = '\n'.join(diff_lines)
|
|
||||||
|
|
||||||
# 统计变更
|
# 统计变更
|
||||||
added = sum(1 for l in diff_lines if l.startswith('+') and not l.startswith('+++'))
|
added = sum(1 for l in diff_lines if l.startswith('+') and not l.startswith('+++'))
|
||||||
@@ -655,7 +662,7 @@ else:
|
|||||||
display = self._build_diff_display(
|
display = self._build_diff_display(
|
||||||
basename, filepath,
|
basename, filepath,
|
||||||
before_numbered, after_numbered,
|
before_numbered, after_numbered,
|
||||||
diff_text, added, removed,
|
diff_lines, added, removed,
|
||||||
before_count, after_count
|
before_count, after_count
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -670,38 +677,62 @@ else:
|
|||||||
|
|
||||||
def _build_diff_display(self, basename, filepath,
|
def _build_diff_display(self, basename, filepath,
|
||||||
before_numbered, after_numbered,
|
before_numbered, after_numbered,
|
||||||
diff_text, added, removed,
|
diff_lines, added, removed,
|
||||||
before_count, after_count):
|
before_count, after_count):
|
||||||
"""构建完整的 diff 显示文本"""
|
"""构建完整的 diff 显示文本(带 ANSI 颜色)"""
|
||||||
sep = '═' * 70
|
sep = '═' * 70
|
||||||
thin_sep = '─' * 70
|
thin_sep = '─' * 70
|
||||||
|
|
||||||
|
# ANSI 颜色
|
||||||
|
R = '\033[31m' # 红
|
||||||
|
G = '\033[32m' # 绿
|
||||||
|
C = '\033[36m' # 青
|
||||||
|
B = '\033[1m' # 粗体
|
||||||
|
D = '\033[2m' # 暗
|
||||||
|
W = '\033[37m' # 白
|
||||||
|
N = '\033[0m' # 重置
|
||||||
|
|
||||||
|
# 构建带颜色的 diff
|
||||||
|
diff_colored_lines = []
|
||||||
|
for line in diff_lines:
|
||||||
|
if line.startswith('@@'):
|
||||||
|
diff_colored_lines.append(f"{C}{line}{N}")
|
||||||
|
elif line.startswith('+++') or line.startswith('---'):
|
||||||
|
diff_colored_lines.append(f"{B}{line}{N}")
|
||||||
|
elif line.startswith('+'):
|
||||||
|
diff_colored_lines.append(f"{G}{line}{N}")
|
||||||
|
elif line.startswith('-'):
|
||||||
|
diff_colored_lines.append(f"{R}{line}{N}")
|
||||||
|
else:
|
||||||
|
diff_colored_lines.append(line)
|
||||||
|
diff_colored = '\n'.join(diff_colored_lines) if diff_colored_lines else ' (无差异)'
|
||||||
|
|
||||||
display = f"""
|
display = f"""
|
||||||
{sep}
|
{W}{sep}{N}
|
||||||
文件变更对比: {basename}
|
{B} 文件变更对比: {basename}{N}
|
||||||
路径: {filepath}
|
{D} 路径: {filepath}{N}
|
||||||
{sep}
|
{W}{sep}{N}
|
||||||
|
|
||||||
统计: {before_count} 行 → {after_count} 行 | +{added} 新增 -{removed} 删除
|
统计: {before_count} 行 → {after_count} 行 | {G}+{added} 新增{N} {R}-{removed} 删除{N}
|
||||||
|
|
||||||
{thin_sep}
|
{W}{thin_sep}{N}
|
||||||
修改前 ({before_count} 行):
|
修改前 ({before_count} 行):
|
||||||
{thin_sep}
|
{W}{thin_sep}{N}
|
||||||
{before_numbered}
|
{before_numbered}
|
||||||
|
|
||||||
{thin_sep}
|
{W}{thin_sep}{N}
|
||||||
修改后 ({after_count} 行):
|
修改后 ({after_count} 行):
|
||||||
{thin_sep}
|
{W}{thin_sep}{N}
|
||||||
{after_numbered}
|
{after_numbered}
|
||||||
|
|
||||||
{thin_sep}
|
{W}{thin_sep}{N}
|
||||||
差异 (Diff):
|
差异 (Diff):
|
||||||
{thin_sep}
|
{W}{thin_sep}{N}
|
||||||
{diff_text if diff_text else ' (无差异)'}
|
{diff_colored}
|
||||||
|
|
||||||
{sep}
|
{W}{sep}{N}
|
||||||
按任意键关闭此窗口...
|
按任意键关闭此窗口...
|
||||||
{sep}
|
{W}{sep}{N}
|
||||||
"""
|
"""
|
||||||
return display
|
return display
|
||||||
|
|
||||||
@@ -775,8 +806,81 @@ else:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[Diff 弹窗] 创建失败: {e}")
|
print(f"[Diff 弹窗] 创建失败: {e}")
|
||||||
|
|
||||||
|
def _show_inline_diff(self, filepath: str, before: str, after: str):
|
||||||
|
"""在主终端内显示 diff(SSH 兼容模式),带颜色"""
|
||||||
|
basename = os.path.basename(filepath)
|
||||||
|
is_tui = self.cli and getattr(self.cli, 'tui_output_callback', None)
|
||||||
|
|
||||||
|
# 生成 diff
|
||||||
|
diff_lines = list(difflib.unified_diff(
|
||||||
|
before.splitlines(keepends=True),
|
||||||
|
after.splitlines(keepends=True),
|
||||||
|
fromfile=f"修改前: {basename}",
|
||||||
|
tofile=f"修改后: {basename}",
|
||||||
|
lineterm=''
|
||||||
|
))
|
||||||
|
|
||||||
|
# 统计
|
||||||
|
added = sum(1 for l in diff_lines if l.startswith('+') and not l.startswith('+++'))
|
||||||
|
removed = sum(1 for l in diff_lines if l.startswith('-') and not l.startswith('---'))
|
||||||
|
before_count = len(before.splitlines())
|
||||||
|
after_count = len(after.splitlines())
|
||||||
|
|
||||||
|
sep = '═' * 60
|
||||||
|
thin = '─' * 60
|
||||||
|
|
||||||
|
if is_tui:
|
||||||
|
# TUI 模式:使用 Rich 标记
|
||||||
|
output = self.cli._output
|
||||||
|
output(f"[dim]{sep}[/]")
|
||||||
|
output(f"[bold cyan] 文件变更: {basename}[/] [dim]{filepath}[/]")
|
||||||
|
output(f"[dim]{sep}[/]")
|
||||||
|
output(f"[bold] 统计: {before_count} 行 → {after_count} 行 | "
|
||||||
|
f"[green]+{added} 新增[/] [red]-{removed} 删除[/]")
|
||||||
|
output(f"[dim]{thin}[/]")
|
||||||
|
|
||||||
|
for line in diff_lines:
|
||||||
|
if line.startswith('@@'):
|
||||||
|
output(f"[cyan]{line}[/]")
|
||||||
|
elif line.startswith('+++') or line.startswith('---'):
|
||||||
|
output(f"[bold]{line}[/]")
|
||||||
|
elif line.startswith('+'):
|
||||||
|
output(f"[green]{line}[/]")
|
||||||
|
elif line.startswith('-'):
|
||||||
|
output(f"[red]{line}[/]")
|
||||||
|
else:
|
||||||
|
output(line)
|
||||||
|
|
||||||
|
output(f"[dim]{sep}[/]")
|
||||||
|
else:
|
||||||
|
# CLI 模式:使用 ANSI 颜色
|
||||||
|
from colorama import Fore, Style
|
||||||
|
_out = print
|
||||||
|
|
||||||
|
_out(f"{Fore.WHITE}{sep}{Style.RESET_ALL}")
|
||||||
|
_out(f"{Fore.CYAN} 文件变更: {basename}{Style.RESET_ALL} {filepath}")
|
||||||
|
_out(f"{Fore.WHITE}{sep}{Style.RESET_ALL}")
|
||||||
|
_out(f" 统计: {before_count} 行 → {after_count} 行 | "
|
||||||
|
f"{Fore.GREEN}+{added} 新增{Style.RESET_ALL} "
|
||||||
|
f"{Fore.RED}-{removed} 删除{Style.RESET_ALL}")
|
||||||
|
_out(f"{Fore.WHITE}{thin}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
for line in diff_lines:
|
||||||
|
if line.startswith('@@'):
|
||||||
|
_out(f"{Fore.CYAN}{line}{Style.RESET_ALL}")
|
||||||
|
elif line.startswith('+++') or line.startswith('---'):
|
||||||
|
_out(f"{Style.BRIGHT}{line}{Style.RESET_ALL}")
|
||||||
|
elif line.startswith('+'):
|
||||||
|
_out(f"{Fore.GREEN}{line}{Style.RESET_ALL}")
|
||||||
|
elif line.startswith('-'):
|
||||||
|
_out(f"{Fore.RED}{line}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
_out(line)
|
||||||
|
|
||||||
|
_out(f"{Fore.WHITE}{sep}{Style.RESET_ALL}")
|
||||||
|
|
||||||
def _op_diff_popup(self, path: str, rest: str) -> str:
|
def _op_diff_popup(self, path: str, rest: str) -> str:
|
||||||
"""切换 diff 弹窗开关"""
|
"""切换 diff 弹窗开关 / 设置显示模式"""
|
||||||
arg = (path + ' ' + rest).strip().lower()
|
arg = (path + ' ' + rest).strip().lower()
|
||||||
if arg in ('on', '开', '开启', 'enable', '1', 'true'):
|
if arg in ('on', '开', '开启', 'enable', '1', 'true'):
|
||||||
self.diff_popup_enabled = True
|
self.diff_popup_enabled = True
|
||||||
@@ -784,6 +888,14 @@ else:
|
|||||||
elif arg in ('off', '关', '关闭', 'disable', '0', 'false'):
|
elif arg in ('off', '关', '关闭', 'disable', '0', 'false'):
|
||||||
self.diff_popup_enabled = False
|
self.diff_popup_enabled = False
|
||||||
return " Diff 弹窗已关闭"
|
return " Diff 弹窗已关闭"
|
||||||
|
elif arg in ('popup', '弹窗', '弹窗模式'):
|
||||||
|
self.diff_popup_enabled = True
|
||||||
|
self.diff_popup_mode = True
|
||||||
|
return " Diff 模式: 弹窗(新终端窗口)"
|
||||||
|
elif arg in ('inline', '内联', 'ssh', '终端', '主终端'):
|
||||||
|
self.diff_popup_enabled = True
|
||||||
|
self.diff_popup_mode = False
|
||||||
|
return " Diff 模式: 主终端内显示(SSH 兼容)"
|
||||||
else:
|
else:
|
||||||
# 无参数:切换状态
|
# 无参数:切换状态
|
||||||
self.diff_popup_enabled = not self.diff_popup_enabled
|
self.diff_popup_enabled = not self.diff_popup_enabled
|
||||||
|
|||||||
+43
-3
@@ -174,9 +174,10 @@ class AICLI(BaseAICLI, ClawliMixin, ToolMixin, CodeExecMixin, DisplayMixin, Hist
|
|||||||
- 如果语法检查失败,根据错误信息修复代码后再次编辑
|
- 如果语法检查失败,根据错误信息修复代码后再次编辑
|
||||||
|
|
||||||
【Diff 弹窗 — 自动弹出修改对比】
|
【Diff 弹窗 — 自动弹出修改对比】
|
||||||
- 编辑操作完成后自动弹出新终端窗口,显示修改前后的完整内容和行号对比
|
- 编辑操作完成后自动显示修改前后的完整内容和行号对比
|
||||||
- 包含 diff 差异、新增/删除行数统计
|
- 新增行用绿色标记,删除行用红色标记
|
||||||
- 开关: diff_popup on(开启)/ diff_popup off(关闭)/ diff_popup(切换)
|
- 支持两种模式: 弹窗模式(新终端窗口)/ 主终端内显示(SSH 兼容)
|
||||||
|
- 开关: diff_popup on / diff_popup off / diff_popup popup / diff_popup inline
|
||||||
- 默认开启,无需手动调用
|
- 默认开启,无需手动调用
|
||||||
|
|
||||||
【code_editor 编辑格式】
|
【code_editor 编辑格式】
|
||||||
@@ -1023,8 +1024,47 @@ multi 操作支持一次修改多处:
|
|||||||
if len(loaded_engines) > 1:
|
if len(loaded_engines) > 1:
|
||||||
print(f"{Fore.GREEN}已加载的AI引擎: {', '.join(loaded_engines)}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}已加载的AI引擎: {', '.join(loaded_engines)}{Style.RESET_ALL}")
|
||||||
print(f"{Fore.GREEN}{'-' * 50}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}{'-' * 50}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# 询问 diff 弹窗模式
|
||||||
|
self._ask_diff_popup_mode()
|
||||||
|
|
||||||
self._run_cli_loop()
|
self._run_cli_loop()
|
||||||
|
|
||||||
|
def _ask_diff_popup_mode(self):
|
||||||
|
"""启动时询问 diff 显示模式"""
|
||||||
|
# 找到 code_editor 插件
|
||||||
|
code_editor = None
|
||||||
|
for tool in self.liugin_manager.tools:
|
||||||
|
if tool.get('name') == 'code_editor':
|
||||||
|
code_editor = tool.get('handler')
|
||||||
|
break
|
||||||
|
|
||||||
|
if code_editor is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 找到插件实例(通过 handler 的 __self__)
|
||||||
|
plugin_instance = getattr(code_editor, '__self__', None)
|
||||||
|
if plugin_instance is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"\n{Fore.CYAN} Diff 显示模式(AI 修改文件后的对比方式):{Style.RESET_ALL}")
|
||||||
|
print(f" {Fore.WHITE}1{Style.RESET_ALL} - 弹窗模式(新终端窗口显示)")
|
||||||
|
print(f" {Fore.WHITE}2{Style.RESET_ALL} - 主终端内显示(SSH 兼容,推荐)")
|
||||||
|
|
||||||
|
try:
|
||||||
|
choice = input(f"\n{Fore.WHITE} 请选择 [1/2](默认 2): {Style.RESET_ALL}").strip()
|
||||||
|
except (EOFError, KeyboardInterrupt):
|
||||||
|
choice = '2'
|
||||||
|
|
||||||
|
if choice == '1':
|
||||||
|
plugin_instance.diff_popup_mode = True
|
||||||
|
print(f"{Fore.GREEN} ✓ 已选择: 弹窗模式{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
plugin_instance.diff_popup_mode = False
|
||||||
|
print(f"{Fore.GREEN} ✓ 已选择: 主终端内显示{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
def _run_cli_loop(self):
|
def _run_cli_loop(self):
|
||||||
"""运行 CLI 主循环"""
|
"""运行 CLI 主循环"""
|
||||||
while True:
|
while True:
|
||||||
|
|||||||
Reference in new issue
Block a user