- Complete architectural overhaul of useUnifiedCompletion hook - Unified state management: 8 separate states → single CompletionState interface - Simplified core logic: getWordAtCursor 194 lines → 42 lines (78% reduction) - Fixed infinite React update loops with ref-based input tracking - Smart triggering mechanism replacing aggressive auto-completion - Integrated @agent and @file mention system with system reminders - Added comprehensive agent loading and mention processing - Enhanced Tab/Arrow/Enter key handling with clean event management - Maintained 100% functional compatibility across all completion types Key improvements: • File path completion (relative, absolute, ~expansion, @references) • Slash command completion (/help, /model, etc.) • Agent completion (@agent-xxx with intelligent descriptions) • System command completion (PATH scanning with fallback) • Terminal-style Tab cycling, Enter confirmation, Escape cancellation • Preview mode with boundary calculation • History navigation compatibility • Empty directory handling with user feedback Architecture: Event-driven @mention detection → system reminder injection → LLM tool usage Performance: Eliminated 7-layer nested conditionals, reduced state synchronization issues Reliability: Fixed maximum update depth exceeded warnings, stable state management
308 lines
8.4 KiB
Markdown
308 lines
8.4 KiB
Markdown
# 输入框补全系统完整分析
|
||
|
||
## 一、补全系统架构概览
|
||
|
||
### 核心组件
|
||
- **useUnifiedCompletion Hook**: 统一补全系统的核心
|
||
- **三种补全类型**: 文件路径、系统命令、slash命令
|
||
- **触发机制**: Tab键手动触发 + 特殊字符自动触发
|
||
- **Terminal行为模式**: 模仿终端Tab补全行为
|
||
|
||
## 二、补全算法详细分析
|
||
|
||
### 1. 文件路径补全
|
||
|
||
#### 触发条件 (getWordAtCursor)
|
||
```typescript
|
||
// Line 108-125: 处理以/结尾的输入
|
||
if (lastChar === '/') {
|
||
if (input === '/') {
|
||
// 单独/视为slash命令
|
||
return { type: 'command', prefix: '', startPos: 0, endPos: 1 }
|
||
}
|
||
// 其他所有/情况都是文件路径 (src/, ./, ../, /usr/)
|
||
return { type: 'file', prefix: fullPath, startPos: pathStart, endPos: input.length }
|
||
}
|
||
|
||
// Line 161-163: 包含路径特征的词
|
||
if (word.startsWith('/') && !isSlashCommand) {
|
||
return { type: 'file', prefix: word, startPos: start, endPos: end }
|
||
}
|
||
|
||
// Line 182-189: 文件命令后的参数
|
||
const afterFileCommand = /\b(cat|ls|cd|vim|code|open|read|edit|...)\s*$/.test(beforeWord)
|
||
const hasPathChars = word.includes('/') || word.includes('.') || word.startsWith('~')
|
||
```
|
||
|
||
#### 生成算法 (generateFileSuggestions)
|
||
```typescript
|
||
// Line 439-536
|
||
1. 解析路径(支持~扩展)
|
||
2. 检查是否@引用路径(特殊处理)
|
||
3. 读取目录内容
|
||
4. 过滤匹配的文件/目录
|
||
5. 生成正确的补全值(处理复杂路径情况)
|
||
```
|
||
|
||
#### 🔴 问题1: 路径拼接逻辑复杂易错
|
||
```typescript
|
||
// Line 483-521: 极其复杂的路径拼接逻辑
|
||
if (prefix.includes('/')) {
|
||
if (prefix.endsWith('/')) {
|
||
value = prefix + entry + (isDir ? '/' : '')
|
||
} else {
|
||
if (existsSync(absolutePath) && statSync(absolutePath).isDirectory()) {
|
||
value = prefix + '/' + entry + (isDir ? '/' : '')
|
||
} else {
|
||
// 更复杂的部分路径补全...
|
||
}
|
||
}
|
||
}
|
||
```
|
||
**问题**: 多层嵌套if-else,边界条件处理不一致
|
||
|
||
#### 🔴 问题2: @引用路径处理不完整
|
||
```typescript
|
||
// Line 448-452
|
||
if (searchPath.startsWith('@')) {
|
||
actualSearchPath = searchPath.slice(1) // 简单去掉@
|
||
}
|
||
```
|
||
**问题**: @引用应该保持语义,但文件系统查找又需要去掉@,导致混乱
|
||
|
||
### 2. 系统命令补全
|
||
|
||
#### 加载机制 (loadSystemCommands)
|
||
```typescript
|
||
// Line 201-254
|
||
1. 从$PATH环境变量获取目录列表
|
||
2. 扫描每个目录的可执行文件
|
||
3. 使用fallback命令列表兜底
|
||
4. 缓存结果避免重复扫描
|
||
```
|
||
|
||
#### 生成算法 (generateUnixCommandSuggestions)
|
||
```typescript
|
||
// Line 289-315
|
||
1. 过滤匹配前缀的命令
|
||
2. 限制最多20个结果
|
||
3. 使用◆符号标记系统命令
|
||
4. score为85(低于agent的90)
|
||
```
|
||
|
||
#### 🔴 问题3: 系统命令检测不准确
|
||
```typescript
|
||
// Line 228-232
|
||
if (stats.isFile() && (stats.mode & 0o111) !== 0) {
|
||
commandSet.add(entry)
|
||
}
|
||
```
|
||
**问题**: 仅通过文件权限判断可执行性,在Windows/Mac上可能不准确
|
||
|
||
#### 🔴 问题4: 系统命令与文件补全混淆
|
||
```typescript
|
||
// Line 309
|
||
type: 'file' as const, // 系统命令被标记为file类型
|
||
```
|
||
**问题**: 类型混用导致逻辑混乱
|
||
|
||
### 3. Slash命令补全
|
||
|
||
#### 触发条件
|
||
```typescript
|
||
// Line 145-159
|
||
if (word.startsWith('/')) {
|
||
if (beforeWord === '' && word === '/') {
|
||
// 单独/显示所有命令
|
||
return { type: 'command', prefix: '', ... }
|
||
} else if (beforeWord === '' && /^\/[a-zA-Z]*$/.test(word)) {
|
||
// /help, /model等
|
||
return { type: 'command', prefix: word.slice(1), ... }
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 生成算法 (generateCommandSuggestions)
|
||
```typescript
|
||
// Line 262-286
|
||
1. 过滤隐藏命令
|
||
2. 匹配前缀(包括别名)
|
||
3. 返回带/前缀的命令名
|
||
4. score基于匹配程度
|
||
```
|
||
|
||
#### 🔴 问题5: Slash命令与绝对路径冲突
|
||
```typescript
|
||
// Line 111-113
|
||
if (input === '/') {
|
||
return { type: 'command', ... } // 可能是绝对路径的开始
|
||
}
|
||
```
|
||
**问题**: `/usr/bin`会被误判为slash命令开始
|
||
|
||
### 4. @Agent补全(扩展功能)
|
||
|
||
#### 触发和生成
|
||
```typescript
|
||
// Line 166-176: @触发检测
|
||
if (word.startsWith('@')) {
|
||
return { type: 'agent', prefix: word.slice(1), ... }
|
||
}
|
||
|
||
// Line 543-587: 混合agent和文件建议
|
||
const agentSuggestions = generateAgentSuggestions(context.prefix)
|
||
const fileSuggestions = generateFileSuggestions(context.prefix)
|
||
// 混合显示,agent优先
|
||
```
|
||
|
||
#### 🔴 问题6: @符号语义不一致
|
||
**问题**: @既用于agent引用,又用于文件引用,导致混淆
|
||
|
||
## 三、Tab键行为分析
|
||
|
||
### Terminal兼容行为
|
||
```typescript
|
||
// Line 654-745: Tab键处理逻辑
|
||
1. 无匹配 → 让Tab通过
|
||
2. 单个匹配 → 立即补全
|
||
3. 多个匹配 → 检查公共前缀或显示菜单
|
||
4. 菜单显示时 → 循环选择
|
||
```
|
||
|
||
#### 🔴 问题7: Preview模式边界计算错误
|
||
```typescript
|
||
// Line 684-689
|
||
const currentTail = input.slice(originalContext.startPos)
|
||
const nextSpaceIndex = currentTail.indexOf(' ')
|
||
const afterPos = nextSpaceIndex === -1 ? '' : currentTail.slice(nextSpaceIndex)
|
||
```
|
||
**问题**: 在输入变化后,原始context位置可能不准确
|
||
|
||
## 四、自动触发机制
|
||
|
||
### 触发条件 (shouldAutoTrigger)
|
||
```typescript
|
||
// Line 1141-1155
|
||
case 'command': return true // /总是触发
|
||
case 'agent': return true // @总是触发
|
||
case 'file': return context.prefix.includes('/') ||
|
||
context.prefix.includes('.') ||
|
||
context.prefix.startsWith('~')
|
||
```
|
||
|
||
#### 🔴 问题8: 过度触发
|
||
**问题**: 任何包含/的输入都会触发文件补全,包括URL、正则表达式等
|
||
|
||
## 五、复杂边界条件问题汇总
|
||
|
||
### 🔴 严重问题
|
||
|
||
1. **路径补全在复杂嵌套目录下失效**
|
||
- `src/tools/../../utils/` 无法正确解析
|
||
- 符号链接处理不当
|
||
|
||
2. **空格路径处理缺失**
|
||
- `"My Documents/"` 无法补全
|
||
- 需要引号包裹的路径无法识别
|
||
|
||
3. **Windows路径不兼容**
|
||
- `C:\Users\` 无法识别
|
||
- 反斜杠路径完全不支持
|
||
|
||
4. **并发状态管理混乱**
|
||
- 快速输入时状态更新不同步
|
||
- Preview模式与实际输入不一致
|
||
|
||
5. **目录权限处理不当**
|
||
- 无权限目录导致崩溃
|
||
- 空目录消息显示后立即消失
|
||
|
||
### 🟡 中等问题
|
||
|
||
6. **系统命令缓存永不刷新**
|
||
- 新安装的命令无法识别
|
||
- PATH变化不会更新
|
||
|
||
7. **@引用语义混乱**
|
||
- @agent-xxx vs @src/file.ts
|
||
- 补全后@符号处理不一致
|
||
|
||
8. **Space键行为不一致**
|
||
- 目录继续导航 vs 文件结束补全
|
||
- 逻辑判断复杂易错
|
||
|
||
9. **History导航破坏补全状态**
|
||
- 上下箭头切换历史时补全面板残留
|
||
- isHistoryNavigation判断不准确
|
||
|
||
10. **删除键抑制机制过于简单**
|
||
- 100ms固定延迟不适合所有场景
|
||
- 可能导致正常触发被误抑制
|
||
|
||
### 🟢 优化建议
|
||
|
||
1. **简化路径拼接逻辑**
|
||
- 使用path.join统一处理
|
||
- 分离绝对/相对路径逻辑
|
||
|
||
2. **明确类型系统**
|
||
- 系统命令应有独立type
|
||
- @引用应有明确的子类型
|
||
|
||
3. **改进触发机制**
|
||
- 增加上下文感知(代码 vs 文本)
|
||
- 可配置的触发规则
|
||
|
||
4. **优化性能**
|
||
- 限制文件系统访问频率
|
||
- 使用虚拟滚动处理大量建议
|
||
|
||
5. **增强错误处理**
|
||
- 权限错误优雅降级
|
||
- 异步操作超时控制
|
||
|
||
## 六、核心设计缺陷
|
||
|
||
### 1. 过度复杂的条件判断
|
||
- 483-521行的路径拼接有7层嵌套
|
||
- 难以理解和维护
|
||
|
||
### 2. 类型系统滥用
|
||
- 系统命令使用file类型
|
||
- agent和file共享@触发器
|
||
|
||
### 3. 状态管理混乱
|
||
- terminalState、lastTabContext、suggestions等多个状态源
|
||
- 同步更新困难
|
||
|
||
### 4. 缺乏抽象层
|
||
- 直接操作文件系统
|
||
- 没有统一的补全提供者接口
|
||
|
||
## 七、改进方案建议
|
||
|
||
```typescript
|
||
// 建议的架构
|
||
interface CompletionProvider {
|
||
trigger: RegExp | string
|
||
canProvide(context: Context): boolean
|
||
provide(context: Context): Promise<Suggestion[]>
|
||
}
|
||
|
||
class FileCompletionProvider implements CompletionProvider { }
|
||
class CommandCompletionProvider implements CompletionProvider { }
|
||
class SystemCommandProvider implements CompletionProvider { }
|
||
class AgentCompletionProvider implements CompletionProvider { }
|
||
|
||
// 统一管理
|
||
class CompletionManager {
|
||
providers: CompletionProvider[]
|
||
async getSuggestions(context: Context) {
|
||
const applicable = providers.filter(p => p.canProvide(context))
|
||
const results = await Promise.all(applicable.map(p => p.provide(context)))
|
||
return merge(results)
|
||
}
|
||
}
|
||
```
|
||
|
||
这样可以解决类型混淆、逻辑耦合、扩展困难等核心问题。 |