# Codexlens LSP API 规范 **版本**: 1.1 **状态**: ✅ APPROVED (Gemini Review) **架构**: codexlens 提供 Python API,CCW 实现 MCP 端点 **分析来源**: Gemini (架构评审) + Codex (实现评审) **最后更新**: 2025-01-17 --- ## 一、概述 ### 1.1 背景 基于 cclsp MCP 服务器实现的分析,设计 codexlens 的 LSP 搜索方法接口,为 AI 提供代码智能能力。 ### 1.2 架构决策 **MCP 端点由 CCW 实现,codexlens 只提供 Python API** ``` ┌─────────────────────────────────────────────────────────────┐ │ Claude Code │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ MCP Client │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ CCW MCP Server │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ MCP Tool Handlers │ │ │ │ │ │ • codexlens_file_context │ │ │ │ │ │ • codexlens_find_definition │ │ │ │ │ │ • codexlens_find_references │ │ │ │ │ │ • codexlens_semantic_search │ │ │ │ │ └──────────────────────┬──────────────────────────┘ │ │ │ └─────────────────────────┼─────────────────────────────┘ │ └────────────────────────────┼────────────────────────────────┘ │ Python API 调用 ▼ ┌─────────────────────────────────────────────────────────────┐ │ codexlens │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ Public API Layer │ │ │ │ codexlens.api.file_context() │ │ │ │ codexlens.api.find_definition() │ │ │ │ codexlens.api.find_references() │ │ │ │ codexlens.api.semantic_search() │ │ │ └──────────────────────┬────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────▼────────────────────────────────┐ │ │ │ Core Components │ │ │ │ GlobalSymbolIndex | ChainSearchEngine | HoverProvider │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────▼────────────────────────────────┐ │ │ │ SQLite Index Databases │ │ │ │ global_symbols.db | *.index.db (per-directory) │ │ │ └───────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ### 1.3 职责分离 | 组件 | 职责 | |------|------| | **codexlens** | Python API、索引查询、搜索算法、结果聚合、降级处理 | | **CCW** | MCP 协议、参数校验、结果序列化、错误处理、project_root 推断 | ### 1.4 codexlens vs cclsp 对比 | 特性 | cclsp | codexlens | |------|-------|-----------| | 数据源 | 实时 LSP 服务器 | 预建 SQLite 索引 | | 启动时间 | 200-3000ms | <50ms | | 响应时间 | 50-500ms | <5ms | | 跨语言 | 每语言需要 LSP 服务器 | 统一 Python/TS/JS/Go 索引 | | 依赖 | 需要语言服务器 | 无外部依赖 | | 准确度 | 100% (编译器级) | 95%+ (tree-sitter) | | 重命名支持 | 是 | 否 (只读索引) | | 实时诊断 | 是 | 通过 IDE MCP | **推荐**: codexlens 用于快速搜索,cclsp 用于精确重构 --- ## 二、cclsp 设计模式 (参考) ### 2.1 MCP 工具接口设计 | 模式 | 说明 | 代码位置 | |------|------|----------| | **基于名称** | 接受 `symbol_name` 而非文件坐标 | `index.ts:70` | | **安全消歧义** | `rename_symbol` → `rename_symbol_strict` 两步 | `index.ts:133, 172` | | **复杂性抽象** | 隐藏 LSP 协议细节 | `index.ts:211` | | **优雅失败** | 返回有用的文本响应 | 全局 | ### 2.2 符号解析算法 ``` 1. getDocumentSymbols (lsp-client.ts:1406) └─ 获取文件所有符号 2. 处理两种格式: ├─ DocumentSymbol[] → 扁平化 └─ SymbolInformation[] → 二次定位 3. 过滤: symbol.name === symbolName && symbol.kind 4. 回退: 无结果时移除 kind 约束重试 5. 聚合: 遍历所有匹配,聚合定义位置 ``` --- ## 三、需求规格 ### 需求 1: 文件上下文查询 (`file_context`) **用途**: 读取代码文件,返回文件中所有方法的调用关系摘要 **输出示例**: ```markdown ## src/auth/login.py (3 methods) ### login_user (line 15-45) - Calls: validate_password (auth/utils.py:23), create_session (session/manager.py:89) - Called by: handle_login_request (api/routes.py:156), test_login (tests/test_auth.py:34) ### validate_token (line 47-62) - Calls: decode_jwt (auth/jwt.py:12) - Called by: auth_middleware (middleware/auth.py:28) ``` ### 需求 2: 通用 LSP 搜索 (cclsp 兼容) | 端点 | 用途 | |------|------| | `find_definition` | 根据符号名查找定义位置 | | `find_references` | 查找符号的所有引用 | | `workspace_symbols` | 工作区符号搜索 | | `get_hover` | 获取符号悬停信息 | ### 需求 3: 向量 + LSP 融合搜索 **用途**: 结合向量语义搜索和结构化 LSP 搜索 **融合策略**: - **RRF** (首选): 简单、不需要分数归一化、鲁棒 - **Cascade**: 特定场景,先向量后 LSP - **Adaptive**: 长期目标,按查询类型自动选择 --- ## 四、API 规范 ### 4.1 模块结构 ``` src/codexlens/ ├─ api/ [新增] 公开 API 层 │ ├─ __init__.py 导出所有 API │ ├─ file_context.py 文件上下文 │ ├─ definition.py 定义查找 │ ├─ references.py 引用查找 │ ├─ symbols.py 符号搜索 │ ├─ hover.py 悬停信息 │ └─ semantic.py 语义搜索 │ ├─ storage/ │ ├─ global_index.py [扩展] get_file_symbols() │ └─ relationship_query.py [新增] 有向调用查询 │ └─ search/ └─ chain_search.py [修复] schema 兼容 ``` ### 4.2 `codexlens.api.file_context()` ```python from dataclasses import dataclass, field from typing import List, Optional, Dict, Tuple @dataclass class CallInfo: """调用关系信息""" symbol_name: str file_path: Optional[str] # 目标文件 (可能为 None) line: int relationship: str # call | import | inheritance @dataclass class MethodContext: """方法上下文""" name: str kind: str # function | method | class line_range: Tuple[int, int] signature: Optional[str] calls: List[CallInfo] # 出向调用 callers: List[CallInfo] # 入向调用 @dataclass class FileContextResult: """文件上下文结果""" file_path: str language: str methods: List[MethodContext] summary: str # 人类可读摘要 discovery_status: Dict[str, bool] = field(default_factory=lambda: { "outgoing_resolved": False, "incoming_resolved": True, "targets_resolved": False }) def file_context( project_root: str, file_path: str, include_calls: bool = True, include_callers: bool = True, max_depth: int = 1, format: str = "brief" # brief | detailed | tree ) -> FileContextResult: """ 获取代码文件的方法调用上下文。 Args: project_root: 项目根目录 (用于定位索引) file_path: 代码文件路径 include_calls: 是否包含出向调用 include_callers: 是否包含入向调用 max_depth: 调用链深度 (1=直接调用) ⚠️ V1 限制: 当前版本仅支持 max_depth=1 深度调用链分析将在 V2 实现 format: 输出格式 Returns: FileContextResult Raises: IndexNotFoundError: 项目未索引 FileNotFoundError: 文件不存在 Note: V1 实现限制: - max_depth 仅支持 1 (直接调用) - 出向调用目标文件可能为 None (未解析) - 深度调用链分析作为 V2 特性规划 """ ``` ### 4.3 `codexlens.api.find_definition()` ```python @dataclass class DefinitionResult: """定义查找结果""" name: str kind: str file_path: str line: int end_line: int signature: Optional[str] container: Optional[str] # 所属类/模块 score: float def find_definition( project_root: str, symbol_name: str, symbol_kind: Optional[str] = None, file_context: Optional[str] = None, limit: int = 10 ) -> List[DefinitionResult]: """ 根据符号名称查找定义位置。 Fallback 策略: 1. 精确匹配 + kind 过滤 2. 精确匹配 (移除 kind) 3. 前缀匹配 """ ``` ### 4.4 `codexlens.api.find_references()` ```python @dataclass class ReferenceResult: """引用结果""" file_path: str line: int column: int context_line: str relationship: str # call | import | type_annotation | inheritance @dataclass class GroupedReferences: """按定义分组的引用""" definition: DefinitionResult references: List[ReferenceResult] def find_references( project_root: str, symbol_name: str, symbol_kind: Optional[str] = None, include_definition: bool = True, group_by_definition: bool = True, limit: int = 100 ) -> List[GroupedReferences]: """ 查找符号的所有引用位置。 多定义时分组返回,解决引用混淆问题。 """ ``` ### 4.5 `codexlens.api.workspace_symbols()` ```python @dataclass class SymbolInfo: """符号信息""" name: str kind: str file_path: str line: int container: Optional[str] score: float def workspace_symbols( project_root: str, query: str, kind_filter: Optional[List[str]] = None, file_pattern: Optional[str] = None, limit: int = 50 ) -> List[SymbolInfo]: """在整个工作区搜索符号 (前缀匹配)。""" ``` ### 4.6 `codexlens.api.get_hover()` ```python @dataclass class HoverInfo: """悬停信息""" name: str kind: str signature: str documentation: Optional[str] file_path: str line_range: Tuple[int, int] type_info: Optional[str] def get_hover( project_root: str, symbol_name: str, file_path: Optional[str] = None ) -> Optional[HoverInfo]: """获取符号的详细悬停信息。""" ``` ### 4.7 `codexlens.api.semantic_search()` ```python @dataclass class SemanticResult: """语义搜索结果""" symbol_name: str kind: str file_path: str line: int vector_score: Optional[float] structural_score: Optional[float] fusion_score: float snippet: str match_reason: Optional[str] def semantic_search( project_root: str, query: str, mode: str = "fusion", # vector | structural | fusion vector_weight: float = 0.5, structural_weight: float = 0.3, keyword_weight: float = 0.2, fusion_strategy: str = "rrf", # rrf | staged | binary | hybrid kind_filter: Optional[List[str]] = None, limit: int = 20, include_match_reason: bool = False ) -> List[SemanticResult]: """ 语义搜索 - 结合向量和结构化搜索。 Args: project_root: 项目根目录 query: 自然语言查询 mode: 搜索模式 - vector: 仅向量搜索 - structural: 仅结构搜索 (符号 + 关系) - fusion: 融合搜索 (默认) vector_weight: 向量搜索权重 [0, 1] structural_weight: 结构搜索权重 [0, 1] keyword_weight: 关键词搜索权重 [0, 1] fusion_strategy: 融合策略 (映射到 chain_search.py) - rrf: Reciprocal Rank Fusion (推荐,默认) - staged: 分阶段级联 → staged_cascade_search - binary: 二分重排级联 → binary_rerank_cascade_search - hybrid: 混合级联 → hybrid_cascade_search kind_filter: 符号类型过滤 limit: 最大返回数量 include_match_reason: 是否生成匹配原因 (启发式,非 LLM) Returns: 按 fusion_score 排序的结果列表 降级行为: - 无向量索引: vector_score=None, 使用 FTS + 结构搜索 - 无关系数据: structural_score=None, 仅向量搜索 """ ``` --- ## 五、已知问题与解决方案 ### 5.1 P0 阻塞项 | 问题 | 位置 | 解决方案 | |------|------|----------| | **索引 Schema 不匹配** | `chain_search.py:313-324` vs `dir_index.py:304-312` | 兼容 `full_path` 和 `path` | | **文件符号查询缺失** | `global_index.py:214-260` | 新增 `get_file_symbols()` | | **出向调用查询缺失** | `dir_index.py:333-342` | 新增 `RelationshipQuery` | | **关系类型不一致** | `entities.py:74-79` | 规范化 `calls` → `call` | ### 5.2 设计缺陷 (Gemini 发现) | 缺陷 | 影响 | 解决方案 | |------|------|----------| | **调用图不完整** | `file_context` 缺少出向调用 | 新增有向调用 API | | **消歧义未定义** | 多定义时无法区分 | 实现 `rank_by_proximity()` | | **AI 特性成本过高** | `explanation` 需要 LLM | 设为可选,默认关闭 | | **融合参数不一致** | 3 分支但只有 2 权重 | 补充 `keyword_weight` | ### 5.3 消歧义算法 **V1 实现** (基于文件路径接近度): ```python def rank_by_proximity( results: List[DefinitionResult], file_context: str ) -> List[DefinitionResult]: """按文件接近度排序 (V1: 路径接近度)""" def proximity_score(result): # 1. 同目录最高分 if os.path.dirname(result.file_path) == os.path.dirname(file_context): return 100 # 2. 共同路径前缀长度 common = os.path.commonpath([result.file_path, file_context]) return len(common) return sorted(results, key=proximity_score, reverse=True) ``` **V2 增强计划** (基于 import graph 距离): ```python def rank_by_import_distance( results: List[DefinitionResult], file_context: str, import_graph: Dict[str, Set[str]] ) -> List[DefinitionResult]: """按 import graph 距离排序 (V2)""" def import_distance(result): # BFS 计算最短 import 路径 return bfs_shortest_path( import_graph, file_context, result.file_path ) # 组合: 0.6 * import_distance + 0.4 * path_proximity return sorted(results, key=lambda r: ( 0.6 * import_distance(r) + 0.4 * (100 - proximity_score(r)) )) ``` ### 5.4 参考实现: `get_file_symbols()` **位置**: `src/codexlens/storage/global_index.py` ```python def get_file_symbols(self, file_path: str | Path) -> List[Symbol]: """ 获取指定文件中定义的所有符号。 Args: file_path: 文件路径 (相对或绝对) Returns: 按行号排序的符号列表 """ file_path_str = str(Path(file_path).resolve()) with self._lock: conn = self._get_connection() rows = conn.execute( """ SELECT symbol_name, symbol_kind, file_path, start_line, end_line FROM global_symbols WHERE project_id = ? AND file_path = ? ORDER BY start_line """, (self.project_id, file_path_str), ).fetchall() return [ Symbol( name=row["symbol_name"], kind=row["symbol_kind"], range=(row["start_line"], row["end_line"]), file=row["file_path"], ) for row in rows ] ``` --- ## 六、实现计划 ### Phase 0: 基础设施 (16h) | 任务 | 工时 | 说明 | |------|------|------| | 修复 `search_references` schema | 4h | 兼容两种 schema | | 新增 `GlobalSymbolIndex.get_file_symbols()` | 4h | 文件符号查询 (见 5.4) | | 新增 `RelationshipQuery` 类 | 6h | 有向调用查询 | | 关系类型规范化层 | 2h | `calls` → `call` | ### Phase 1: API 层 (48h) | 任务 | 工时 | 复杂度 | |------|------|--------| | `find_definition()` | 4h | S | | `find_references()` | 8h | M | | `workspace_symbols()` | 4h | S | | `get_hover()` | 4h | S | | `file_context()` | 16h | L | | `semantic_search()` | 12h | M | ### Phase 2: 测试与文档 (16h) | 任务 | 工时 | |------|------| | 单元测试 (≥80%) | 8h | | API 文档 | 4h | | 示例代码 | 4h | ### 关键路径 ``` Phase 0.1 (schema fix) ↓ Phase 0.2 (file symbols) → Phase 1.5 (file_context) ↓ Phase 1 (其他 API) ↓ Phase 2 (测试) ``` --- ## 七、测试策略 ### 7.1 单元测试 ```python # test_global_index.py def test_get_file_symbols(): index = GlobalSymbolIndex(":memory:") index.update_file_symbols(project_id=1, file_path="test.py", symbols=[...]) results = index.get_file_symbols("test.py") assert len(results) == 3 # test_relationship_query.py def test_outgoing_calls(): store = DirIndexStore(":memory:") calls = store.get_outgoing_calls("src/auth.py", "login") assert calls[0].relationship == "call" # 已规范化 ``` ### 7.2 Schema 兼容性测试 ```python def test_search_references_both_schemas(): """测试两种 schema 的引用搜索""" # 旧 schema: files(path, ...) # 新 schema: files(full_path, ...) ``` ### 7.3 降级测试 ```python def test_semantic_search_without_vectors(): result = semantic_search(query="auth", mode="fusion") assert result.vector_score is None assert result.fusion_score > 0 ``` --- ## 八、使用示例 ```python from codexlens.api import ( file_context, find_definition, find_references, semantic_search ) # 1. 获取文件上下文 result = file_context( project_root="/path/to/project", file_path="src/auth/login.py", format="brief" ) print(result.summary) # 2. 查找定义 definitions = find_definition( project_root="/path/to/project", symbol_name="UserService", symbol_kind="class" ) # 3. 语义搜索 results = semantic_search( project_root="/path/to/project", query="处理用户登录验证的函数", mode="fusion" ) ``` --- ## 九、CCW 集成 | codexlens API | CCW MCP Tool | |---------------|--------------| | `file_context()` | `codexlens_file_context` | | `find_definition()` | `codexlens_find_definition` | | `find_references()` | `codexlens_find_references` | | `workspace_symbols()` | `codexlens_workspace_symbol` | | `get_hover()` | `codexlens_get_hover` | | `semantic_search()` | `codexlens_semantic_search` | --- ## 十、分析来源 | 工具 | Session ID | 贡献 | |------|------------|------| | Gemini | `1768618654438-gemini` | 架构评审、设计缺陷、融合策略 | | Codex | `1768618658183-codex` | 组件复用、复杂度估算、任务分解 | | Gemini | `1768620615744-gemini` | 最终评审、改进建议、APPROVED | --- ## 十一、版本历史 | 版本 | 日期 | 变更 | |------|------|------| | 1.0 | 2025-01-17 | 初始版本,合并多文档 | | 1.1 | 2025-01-17 | 应用 Gemini 评审改进: V1 限制说明、策略映射、消歧义增强、参考实现 |