#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ CodeGPS Pro - 完全实用版 真正能用的大模型代码眼睛 - 所有功能都能独立工作 """ import gradio as gr import re import difflib from typing import Dict, List, Tuple, Optional from dataclasses import dataclass import json # ==================== 核心引擎 ==================== @dataclass class CodeBlock: start_line: int end_line: int type: str name: str content: str parent: Optional[str] = None class CodeGPSEngine: """核心代码处理引擎""" def __init__(self): self.reset() def reset(self): """重置状态""" self.original_code = "" self.current_code = "" self.lines = [] self.language = "unknown" self.blocks = [] self.history = [] self.is_loaded = False def auto_load(self, code: str) -> str: """自动加载代码(无需手动触发)""" if not code or not code.strip(): self.reset() return "⚠️ 代码为空" self.original_code = code self.current_code = code self.lines = code.split('\n') self.language = self._detect_language(code) self.blocks = self._parse_blocks() self.is_loaded = True return f"✅ 已加载 | 语言:{self.language.upper()} | 行数:{len(self.lines)} | 代码块:{len(self.blocks)}" def _detect_language(self, code: str) -> str: """快速检测语言""" patterns = { 'python': [r'def\s+\w+', r'class\s+\w+', r'import\s+\w+', r':\s*$'], 'javascript': [r'function\s+', r'const\s+\w+\s*=', r'=>', r'console\.'], 'java': [r'public\s+class', r'private\s+\w+', r'System\.'], 'cpp': [r'#include', r'std::', r'int\s+main'], 'go': [r'package\s+', r'func\s+', r':='], 'php': [r'<\?php', r'\$\w+', r'function\s+\w+'], 'ruby': [r'def\s+\w+', r'end\s*$', r'require\s+'], 'rust': [r'fn\s+\w+', r'let\s+\w+', r'impl\s+'], } for lang, pats in patterns.items(): matches = sum(1 for p in pats if re.search(p, code, re.MULTILINE)) if matches >= 2: return lang return 'plaintext' def _parse_blocks(self) -> List[CodeBlock]: """解析代码块""" blocks = [] if self.language == 'python': blocks = self._parse_python() elif self.language in ['javascript', 'java', 'cpp', 'go']: blocks = self._parse_braces() return blocks def _parse_python(self) -> List[CodeBlock]: """解析Python代码""" blocks = [] i = 0 current_class = None while i < len(self.lines): line = self.lines[i] stripped = line.lstrip() indent = len(line) - len(stripped) # 类定义 if re.match(r'class\s+(\w+)', stripped): name = re.match(r'class\s+(\w+)', stripped).group(1) start = i + 1 end = self._find_python_block_end(i, indent) blocks.append(CodeBlock( start_line=start, end_line=end, type='class', name=name, content='\n'.join(self.lines[i:end]) )) current_class = name i = end continue # 函数/方法定义 if re.match(r'def\s+(\w+)', stripped): name = re.match(r'def\s+(\w+)', stripped).group(1) start = i + 1 end = self._find_python_block_end(i, indent) blocks.append(CodeBlock( start_line=start, end_line=end, type='method' if current_class else 'function', name=name, content='\n'.join(self.lines[i:end]), parent=current_class )) i = end continue i += 1 return blocks def _find_python_block_end(self, start: int, base_indent: int) -> int: """查找Python代码块结束位置""" i = start + 1 while i < len(self.lines): line = self.lines[i] if line.strip(): indent = len(line) - len(line.lstrip()) if indent <= base_indent: return i i += 1 return len(self.lines) def _parse_braces(self) -> List[CodeBlock]: """解析花括号语言""" blocks = [] stack = [] patterns = { 'function': r'(?:function\s+(\w+)|(\w+)\s*\([^)]*\)\s*\{|fn\s+(\w+)|func\s+(\w+))', 'class': r'class\s+(\w+)', } for i, line in enumerate(self.lines, 1): # 检测函数 func_match = re.search(patterns['function'], line) if func_match: name = next((g for g in func_match.groups() if g), 'anonymous') stack.append(('function', name, i, 0)) # 检测类 class_match = re.search(patterns['class'], line) if class_match: name = class_match.group(1) stack.append(('class', name, i, 0)) # 花括号计数 open_count = line.count('{') close_count = line.count('}') if stack: stack[-1] = (stack[-1][0], stack[-1][1], stack[-1][2], stack[-1][3] + open_count - close_count) if stack[-1][3] == 0 and (open_count > 0 or close_count > 0): block_type, name, start, _ = stack.pop() blocks.append(CodeBlock( start_line=start, end_line=i, type=block_type, name=name, content='\n'.join(self.lines[start-1:i]) )) return blocks # ==================== 查询功能 ==================== def get_line(self, line_num: int) -> Dict: """获取单行""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} if not 1 <= line_num <= len(self.lines): return {'success': False, 'error': f'❌ 行号超出范围 (1-{len(self.lines)})'} return { 'success': True, 'content': self.lines[line_num - 1], 'formatted': f"第{line_num}行: {self.lines[line_num - 1]}" } def get_lines(self, start: int, end: int) -> Dict: """获取多行""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} total = len(self.lines) if not (1 <= start <= total and 1 <= end <= total): return {'success': False, 'error': f'❌ 行号超出范围 (1-{total})'} if start > end: start, end = end, start lines_output = [] for i in range(max(1, start - 2), min(total + 1, end + 3)): marker = '>>> ' if start <= i <= end else ' ' lines_output.append(f"{marker}{i:4d} | {self.lines[i-1]}") return { 'success': True, 'content': '\n'.join(self.lines[start-1:end]), 'formatted': '\n'.join(lines_output) } def get_function(self, name: str) -> Dict: """获取函数""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} for block in self.blocks: if block.type in ['function', 'method'] and block.name == name: lines_output = [] for i in range(block.start_line, block.end_line + 1): lines_output.append(f"{i:4d} | {self.lines[i-1]}") return { 'success': True, 'content': block.content, 'formatted': f"📍 函数: {name} [第{block.start_line}-{block.end_line}行]\n" + '\n'.join(lines_output) } return {'success': False, 'error': f'❌ 未找到函数: {name}'} def get_class(self, name: str) -> Dict: """获取类""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} for block in self.blocks: if block.type == 'class' and block.name == name: lines_output = [] for i in range(block.start_line, block.end_line + 1): lines_output.append(f"{i:4d} | {self.lines[i-1]}") return { 'success': True, 'content': block.content, 'formatted': f"📦 类: {name} [第{block.start_line}-{block.end_line}行]\n" + '\n'.join(lines_output) } return {'success': False, 'error': f'❌ 未找到类: {name}'} def search_text(self, keyword: str) -> Dict: """搜索文本""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} results = [] for i, line in enumerate(self.lines, 1): if keyword in line: results.append(f"{i:4d} | {line}") if results: return { 'success': True, 'formatted': f"🔍 找到 {len(results)} 处匹配:\n" + '\n'.join(results[:50]) # 最多显示50个 } return {'success': False, 'error': f'❌ 未找到: {keyword}'} def list_all(self) -> str: """列出所有代码块""" if not self.is_loaded: return "❌ 请先输入代码" output = [f"📋 代码结构 [{self.language.upper()}] 共{len(self.lines)}行\n"] if not self.blocks: output.append("⚠️ 未检测到明显的函数/类结构") return '\n'.join(output) # 按类型分组 classes = [b for b in self.blocks if b.type == 'class'] functions = [b for b in self.blocks if b.type == 'function'] methods = [b for b in self.blocks if b.type == 'method'] if classes: output.append("📦 类定义:") for b in classes: output.append(f" ├─ {b.name} (第{b.start_line}-{b.end_line}行)") if functions: output.append("\n🔧 函数定义:") for b in functions: output.append(f" ├─ {b.name}() (第{b.start_line}-{b.end_line}行)") if methods: output.append("\n⚙️ 方法定义:") for b in methods: parent = f" [{b.parent}]" if b.parent else "" output.append(f" ├─ {b.name}(){parent} (第{b.start_line}-{b.end_line}行)") return '\n'.join(output) # ==================== 修改功能 ==================== def replace_line(self, line_num: int, new_content: str) -> Dict: """替换单行""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} if not 1 <= line_num <= len(self.lines): return {'success': False, 'error': f'❌ 行号超出范围 (1-{len(self.lines)})'} old = self.lines[line_num - 1] self.lines[line_num - 1] = new_content self.current_code = '\n'.join(self.lines) self._save_history('replace_line', line_num, old) return { 'success': True, 'message': f'✅ 已替换第{line_num}行', 'diff': self._make_diff(old, new_content) } def replace_lines(self, start: int, end: int, new_content: str) -> Dict: """替换多行""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} total = len(self.lines) if not (1 <= start <= total and 1 <= end <= total): return {'success': False, 'error': f'❌ 行号超出范围 (1-{total})'} if start > end: start, end = end, start old = '\n'.join(self.lines[start-1:end]) new_lines = new_content.split('\n') self.lines[start-1:end] = new_lines self.current_code = '\n'.join(self.lines) self._save_history('replace_lines', (start, end), old) return { 'success': True, 'message': f'✅ 已替换第{start}-{end}行 ({end-start+1}行 → {len(new_lines)}行)', 'diff': self._make_diff(old, new_content) } def insert_after(self, line_num: int, new_content: str) -> Dict: """在指定行后插入""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} if not 0 <= line_num <= len(self.lines): return {'success': False, 'error': f'❌ 行号超出范围 (0-{len(self.lines)})'} new_lines = new_content.split('\n') self.lines[line_num:line_num] = new_lines self.current_code = '\n'.join(self.lines) self._save_history('insert', line_num, None) return { 'success': True, 'message': f'✅ 已在第{line_num}行后插入{len(new_lines)}行' } def delete_lines(self, start: int, end: int) -> Dict: """删除行""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} total = len(self.lines) if not (1 <= start <= total and 1 <= end <= total): return {'success': False, 'error': f'❌ 行号超出范围 (1-{total})'} if start > end: start, end = end, start deleted = '\n'.join(self.lines[start-1:end]) del self.lines[start-1:end] self.current_code = '\n'.join(self.lines) self._save_history('delete', (start, end), deleted) return { 'success': True, 'message': f'✅ 已删除第{start}-{end}行 (共{end-start+1}行)', 'deleted': deleted } def find_replace(self, find_text: str, replace_text: str, max_replace: int = -1) -> Dict: """查找替换""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} old_code = self.current_code count = 0 changes = [] for i, line in enumerate(self.lines): if find_text in line: if max_replace == -1 or count < max_replace: old_line = line new_line = line.replace(find_text, replace_text) self.lines[i] = new_line count += 1 changes.append(f"第{i+1}行: {old_line} → {new_line}") if count > 0: self.current_code = '\n'.join(self.lines) self._save_history('find_replace', find_text, old_code) return { 'success': True, 'message': f'✅ 已替换 {count} 处', 'changes': '\n'.join(changes[:20]) # 最多显示20条 } return {'success': False, 'error': f'❌ 未找到: {find_text}'} def indent_lines(self, start: int, end: int, spaces: int = 4) -> Dict: """缩进调整""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} total = len(self.lines) if not (1 <= start <= total and 1 <= end <= total): return {'success': False, 'error': f'❌ 行号超出范围 (1-{total})'} indent_str = ' ' * abs(spaces) for i in range(start-1, end): if spaces > 0: self.lines[i] = indent_str + self.lines[i] else: # 反向缩进 self.lines[i] = self.lines[i].lstrip()[:abs(spaces)] + self.lines[i].lstrip()[abs(spaces):] self.current_code = '\n'.join(self.lines) self._save_history('indent', (start, end), None) return { 'success': True, 'message': f'✅ 已调整第{start}-{end}行缩进 ({spaces} 空格)' } def comment_lines(self, start: int, end: int, uncomment: bool = False) -> Dict: """注释/取消注释""" if not self.is_loaded: return {'success': False, 'error': '❌ 请先输入代码'} total = len(self.lines) if not (1 <= start <= total and 1 <= end <= total): return {'success': False, 'error': f'❌ 行号超出范围 (1-{total})'} # 根据语言选择注释符 comment_map = { 'python': '#', 'javascript': '//', 'java': '//', 'cpp': '//', 'go': '//', 'php': '//', 'ruby': '#', 'rust': '//', } comment_symbol = comment_map.get(self.language, '#') for i in range(start-1, end): if uncomment: self.lines[i] = self.lines[i].lstrip().lstrip(comment_symbol).lstrip() else: self.lines[i] = f"{comment_symbol} {self.lines[i]}" self.current_code = '\n'.join(self.lines) self._save_history('comment', (start, end), None) action = '取消注释' if uncomment else '注释' return { 'success': True, 'message': f'✅ 已{action}第{start}-{end}行' } def _make_diff(self, old: str, new: str) -> str: """生成差异对比""" diff = difflib.unified_diff( old.split('\n'), new.split('\n'), lineterm='', fromfile='修改前', tofile='修改后' ) return '\n'.join(diff) def _save_history(self, operation: str, target, old_content): """保存历史""" self.history.append({ 'operation': operation, 'target': target, 'old': old_content }) def undo(self) -> Dict: """撤销""" if not self.history: return {'success': False, 'error': '❌ 没有可撤销的操作'} # 简单实现:重新加载原始代码 self.current_code = self.original_code self.lines = self.original_code.split('\n') self.history = [] return {'success': True, 'message': '✅ 已撤销所有修改'} def get_current_code(self) -> str: """获取当前代码""" return self.current_code # ==================== UI界面 ==================== def create_ui(): engine = CodeGPSEngine() # 主题色 theme = gr.themes.Soft( primary_hue="purple", secondary_hue="blue", ) with gr.Blocks(title="CodeGPS Pro", theme=theme) as app: gr.HTML("""

🎯 CodeGPS Pro

大模型的精准代码手术刀 - 立即可用

""") # 状态显示 status_display = gr.Textbox(label="📊 状态", value="等待输入代码...", interactive=False, max_lines=1) with gr.Tabs(): # ========== Tab 1: 代码编辑器 ========== with gr.TabItem("📝 代码编辑器"): with gr.Row(): with gr.Column(scale=1): code_input = gr.Code( label="代码输入区", language="python", lines=30 ) with gr.Row(): load_example_btn = gr.Button("📥 加载示例代码", variant="secondary") clear_code_btn = gr.Button("🗑️ 清空", variant="secondary") with gr.Column(scale=1): gr.Markdown("### 🗺️ 代码结构") structure_output = gr.Textbox( label="", lines=30, interactive=False, show_label=False ) # ========== Tab 2: 精准查询 ========== with gr.TabItem("🔍 查询代码"): gr.Markdown(""" ### 使用方法 - **查询单行**: 输入行号,如 `15` - **查询范围**: 输入 `10-20` 或分别输入起止行号 - **查询函数**: 输入函数名,如 `main` - **查询类**: 输入类名 - **搜索文本**: 输入关键词搜索 """) with gr.Row(): with gr.Column(): gr.Markdown("#### 快捷查询") with gr.Group(): gr.Markdown("**查询单行**") query_line_num = gr.Number(label="行号", value=1, precision=0) query_line_btn = gr.Button("查询单行", variant="primary") with gr.Group(): gr.Markdown("**查询范围**") with gr.Row(): query_start = gr.Number(label="起始行", value=1, precision=0) query_end = gr.Number(label="结束行", value=10, precision=0) query_range_btn = gr.Button("查询范围", variant="primary") with gr.Group(): gr.Markdown("**查询函数/类**") query_name = gr.Textbox(label="名称", placeholder="如: main") with gr.Row(): query_func_btn = gr.Button("查询函数") query_class_btn = gr.Button("查询类") with gr.Group(): gr.Markdown("**搜索文本**") search_keyword = gr.Textbox(label="关键词", placeholder="如: print") search_btn = gr.Button("搜索", variant="primary") with gr.Column(): query_result = gr.Textbox( label="📍 查询结果", lines=35, interactive=False, elem_classes="code-display" ) # ========== Tab 3: 精准修改 ========== with gr.TabItem("✏️ 修改代码"): with gr.Row(): with gr.Column(): modify_mode = gr.Radio( label="修改模式", choices=[ "替换单行", "替换多行", "插入代码", "删除行", "查找替换", "调整缩进", "添加注释" ], value="替换单行" ) # 通用参数 with gr.Group() as single_line_group: modify_line = gr.Number(label="目标行号", value=1, precision=0) with gr.Group(visible=False) as range_group: with gr.Row(): modify_start = gr.Number(label="起始行", value=1, precision=0) modify_end = gr.Number(label="结束行", value=1, precision=0) with gr.Group(visible=False) as find_replace_group: find_text = gr.Textbox(label="查找文本") replace_text = gr.Textbox(label="替换为") with gr.Group(visible=False) as indent_group: indent_spaces = gr.Slider(-8, 8, value=4, step=4, label="缩进空格数(负数为反向)") new_code_input = gr.Code( label="新代码内容", language="python", lines=10 ) with gr.Row(): execute_modify_btn = gr.Button("🚀 执行修改", variant="primary", scale=2) undo_btn = gr.Button("↩️ 撤销全部", variant="secondary", scale=1) with gr.Column(): modify_result = gr.Textbox( label="📊 修改结果", lines=10, interactive=False ) diff_output = gr.Textbox( label="🔄 变更对比", lines=25, interactive=False, elem_classes="code-display" ) # ========== Tab 4: 导出与预览 ========== with gr.TabItem("📤 导出代码"): with gr.Column(): gr.Markdown("### 当前完整代码") refresh_btn = gr.Button("🔄 刷新预览", variant="primary") current_code_output = gr.Code( label="", language="python", lines=40, show_label=False ) with gr.Row(): download_btn = gr.File(label="下载代码文件") gr.Markdown("### 📊 修改统计") stats_output = gr.Textbox( label="", lines=5, interactive=False ) # ==================== 事件绑定 ==================== # 自动加载代码(核心) def auto_load_handler(code): status = engine.auto_load(code) structure = engine.list_all() if engine.is_loaded else "" return status, structure code_input.change( fn=auto_load_handler, inputs=[code_input], outputs=[status_display, structure_output] ) # 加载示例 def load_example(): example = '''def calculate_sum(a, b): """计算两数之和""" return a + b def calculate_average(numbers): """计算平均值""" if not numbers: return 0 return sum(numbers) / len(numbers) class Calculator: def __init__(self): self.result = 0 def add(self, value): """加法""" self.result += value return self.result def subtract(self, value): """减法""" self.result -= value return self.result def reset(self): """重置""" self.result = 0 if __name__ == "__main__": calc = Calculator() calc.add(10) calc.add(5) print(f"结果: {calc.result}") ''' return example load_example_btn.click( fn=load_example, outputs=[code_input] ) clear_code_btn.click( fn=lambda: "", outputs=[code_input] ) # 查询功能 def query_line(line_num): result = engine.get_line(int(line_num)) if result['success']: return result['formatted'] return result['error'] query_line_btn.click( fn=query_line, inputs=[query_line_num], outputs=[query_result] ) def query_range(start, end): result = engine.get_lines(int(start), int(end)) if result['success']: return result['formatted'] return result['error'] query_range_btn.click( fn=query_range, inputs=[query_start, query_end], outputs=[query_result] ) def query_function(name): result = engine.get_function(name) if result['success']: return result['formatted'] return result['error'] query_func_btn.click( fn=query_function, inputs=[query_name], outputs=[query_result] ) def query_class(name): result = engine.get_class(name) if result['success']: return result['formatted'] return result['error'] query_class_btn.click( fn=query_class, inputs=[query_name], outputs=[query_result] ) def search_text_handler(keyword): result = engine.search_text(keyword) if result['success']: return result['formatted'] return result['error'] search_btn.click( fn=search_text_handler, inputs=[search_keyword], outputs=[query_result] ) # 修改模式切换 def update_modify_ui(mode): if mode in ["替换多行", "删除行", "调整缩进", "添加注释"]: return ( gr.update(visible=False), # single_line_group gr.update(visible=True), # range_group gr.update(visible=False), # find_replace_group gr.update(visible=mode=="调整缩进"), # indent_group gr.update(visible=mode not in ["删除行", "调整缩进", "添加注释"]) # new_code_input ) elif mode == "查找替换": return ( gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False) ) else: # 替换单行, 插入代码 return ( gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True) ) modify_mode.change( fn=update_modify_ui, inputs=[modify_mode], outputs=[single_line_group, range_group, find_replace_group, indent_group, new_code_input] ) # 执行修改 def execute_modify(mode, line, start, end, new_code, find, replace, indent): result = None if mode == "替换单行": result = engine.replace_line(int(line), new_code) elif mode == "替换多行": result = engine.replace_lines(int(start), int(end), new_code) elif mode == "插入代码": result = engine.insert_after(int(line), new_code) elif mode == "删除行": result = engine.delete_lines(int(start), int(end)) elif mode == "查找替换": result = engine.find_replace(find, replace) elif mode == "调整缩进": result = engine.indent_lines(int(start), int(end), int(indent)) elif mode == "添加注释": result = engine.comment_lines(int(start), int(end)) if result and result['success']: # 更新代码编辑器 new_full_code = engine.get_current_code() status = engine.auto_load(new_full_code) diff = result.get('diff', result.get('changes', '')) return ( result.get('message', '✅ 操作成功'), diff, new_full_code, status ) return ( result.get('error', '❌ 操作失败') if result else '❌ 未知错误', '', code_input.value, status_display.value ) execute_modify_btn.click( fn=execute_modify, inputs=[ modify_mode, modify_line, modify_start, modify_end, new_code_input, find_text, replace_text, indent_spaces ], outputs=[modify_result, diff_output, code_input, status_display] ) # 撤销 def undo_handler(): result = engine.undo() if result['success']: code = engine.get_current_code() status = engine.auto_load(code) return result['message'], code, status return result['error'], code_input.value, status_display.value undo_btn.click( fn=undo_handler, outputs=[modify_result, code_input, status_display] ) # 刷新预览 def refresh_preview(): code = engine.get_current_code() stats = f"总行数: {len(engine.lines)}\n修改次数: {len(engine.history)}\n语言: {engine.language}" return code, stats refresh_btn.click( fn=refresh_preview, outputs=[current_code_output, stats_output] ) return app if __name__ == "__main__": app = create_ui() app.launch(server_name="0.0.0.0", server_port=7860, share=True)