Kode-cli/docs/ELEGANT_TAB_IMPROVEMENT_PLAN.md
CrazyBoyM 926df2cfaf feat: Ultra-redesign completion system with @mention integration
- 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
2025-08-21 01:21:12 +08:00

270 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 优雅的Tab补全改进计划
## 一、当前架构分析
### 核心数据结构(保持不变)
```typescript
interface UnifiedSuggestion // ✅ 完美,不需要改动
interface CompletionContext // ✅ 完美,不需要改动
```
### 状态管理(需要增强)
```typescript
// 当前状态
const [suggestions, setSuggestions] // ✅ 保持
const [selectedIndex, setSelectedIndex] // ✅ 保持
const [isActive, setIsActive] // ✅ 保持
const lastTabContext = useRef() // ✅ 保持
// 需要添加的状态(最小化)
const tabState = useRef<TabState>() // 🆕 Tab按键状态
```
### 关键函数(大部分保持)
- `getWordAtCursor()` ✅ 完美,不改
- `generateCommandSuggestions()` ✅ 完美,不改
- `generateAgentSuggestions()` ✅ 完美,不改
- `generateFileSuggestions()` ✅ 完美,不改
- `generateSuggestions()` ✅ 完美,不改
- Tab处理逻辑 ❌ 需要重构
## 二、最小化改动方案
### 1. 添加Tab状态跟踪新增数据结构
```typescript
// 添加到文件顶部与其他interface并列
interface TabState {
lastTabTime: number
consecutiveTabCount: number
lastPrefix: string
lastSuggestions: UnifiedSuggestion[]
}
```
### 2. 添加公共前缀计算(纯函数,无副作用)
```typescript
// 添加为独立的utility函数
const findCommonPrefix = (suggestions: UnifiedSuggestion[]): string => {
if (suggestions.length === 0) return ''
if (suggestions.length === 1) return suggestions[0].value
const values = suggestions.map(s => s.value)
let prefix = values[0]
for (let i = 1; i < values.length; i++) {
while (prefix && !values[i].startsWith(prefix)) {
prefix = prefix.slice(0, -1)
}
if (!prefix) break
}
return prefix
}
```
### 3. 重构Tab处理逻辑核心改动
将现有的Tab处理185-237行替换为新的智能处理
```typescript
// Handle Tab key - Terminal-compliant behavior
useInput(async (_, key) => {
if (!key.tab || key.shift) return false
const context = getWordAtCursor()
if (!context) return false
const now = Date.now()
const isDoubleTab = tabState.current &&
(now - tabState.current.lastTabTime) < 500 &&
tabState.current.lastPrefix === context.prefix
// 如果菜单已显示Tab选择下一个
if (isActive && suggestions.length > 0) {
// 保持原有逻辑
const selected = suggestions[selectedIndex]
// ... 完成逻辑
return true
}
// 生成建议(只在需要时)
let currentSuggestions = suggestions
if (!isDoubleTab || suggestions.length === 0) {
currentSuggestions = await generateSuggestions(context)
}
// 决策树 - 完全符合终端行为
if (currentSuggestions.length === 0) {
// 无匹配:蜂鸣
return false
} else if (currentSuggestions.length === 1) {
// 唯一匹配:立即完成
completeWith(currentSuggestions[0], context)
resetTabState()
return true
} else {
// 多个匹配
const commonPrefix = findCommonPrefix(currentSuggestions)
if (commonPrefix.length > context.prefix.length) {
// 可以补全到公共前缀
partialComplete(commonPrefix, context)
updateTabState(now, context.prefix, currentSuggestions)
return true
} else if (isDoubleTab) {
// 第二次Tab显示菜单
setSuggestions(currentSuggestions)
setIsActive(true)
setSelectedIndex(0)
return true
} else {
// 第一次Tab但无法补全记录状态
updateTabState(now, context.prefix, currentSuggestions)
return false // 蜂鸣
}
}
})
```
### 4. 添加辅助函数(与现有风格一致)
```typescript
// 完成补全
const completeWith = useCallback((suggestion: UnifiedSuggestion, context: CompletionContext) => {
const completion = context.type === 'command' ? `/${suggestion.value} ` :
context.type === 'agent' ? `@${suggestion.value} ` :
suggestion.value
const newInput = input.slice(0, context.startPos) + completion + input.slice(context.endPos)
onInputChange(newInput)
setCursorOffset(context.startPos + completion.length)
}, [input, onInputChange, setCursorOffset])
// 部分补全
const partialComplete = useCallback((prefix: string, context: CompletionContext) => {
const newInput = input.slice(0, context.startPos) + prefix + input.slice(context.endPos)
onInputChange(newInput)
setCursorOffset(context.startPos + prefix.length)
}, [input, onInputChange, setCursorOffset])
// Tab状态管理
const updateTabState = useCallback((time: number, prefix: string, suggestions: UnifiedSuggestion[]) => {
tabState.current = {
lastTabTime: time,
consecutiveTabCount: (tabState.current?.consecutiveTabCount || 0) + 1,
lastPrefix: prefix,
lastSuggestions: suggestions
}
}, [])
const resetTabState = useCallback(() => {
tabState.current = null
}, [])
```
## 三、实施步骤
### Phase 1: 基础设施(不影响现有功能)
1. 添加 `TabState` interface
2. 添加 `tabState` useRef
3. 添加 `findCommonPrefix` 函数
4. 添加辅助函数
### Phase 2: 核心逻辑替换(原子操作)
1. 备份现有Tab处理代码
2. 替换为新的决策树逻辑
3. 测试所有场景
### Phase 3: 细节优化
1. 调整超时时间500ms vs 300ms
2. 优化菜单显示格式
3. 添加蜂鸣反馈(可选)
## 四、影响评估
### 不变的部分90%
- 所有数据结构
- 所有生成函数
- 箭头键处理
- Effect清理逻辑
- 与PromptInput的接口
### 改变的部分10%
- Tab按键处理逻辑
- 新增4个小函数
- 新增1个状态ref
### 风险评估
- **低风险**:改动集中在一处
- **可回滚**:逻辑独立,易于回滚
- **向后兼容**:接口不变
## 五、测试场景
### 场景1: 多个文件补全
```bash
# 文件: package.json, package-lock.json
输入: p[Tab]
期望: 补全到 "package"
输入: package[Tab][Tab]
期望: 显示菜单
```
### 场景2: 唯一匹配
```bash
输入: READ[Tab]
期望: 补全到 "README.md"
```
### 场景3: 连续补全
```bash
输入: src/[Tab]
期望: 可以继续Tab补全
```
## 六、代码风格指南
### 保持一致性
- 使用 `useCallback` 包装所有函数
- 使用 `as const` 断言类型
- 保持简洁的注释风格
### 命名规范
- 动词开头:`completeWith`, `updateTabState`
- 布尔值:`isDoubleTab`, `isActive`
- 常量大写:`TAB_TIMEOUT`
### 错误处理
- 保持静默失败(符合现有风格)
- 使用 try-catch 包装文件操作
## 七、预期效果
### Before
```
cat p[Tab]
▸ package.json # 立即显示菜单 ❌
package-lock.json
```
### After
```
cat p[Tab]
cat package # 补全公共前缀 ✅
cat package[Tab][Tab]
package.json package-lock.json # 双Tab显示 ✅
```
## 八、总结
这个方案:
1. **最小化改动** - 90%代码不变
2. **原子操作** - 可以一次性替换
3. **风格一致** - 像原生代码
4. **100%终端兼容** - 完全匹配bash行为
准备好实施了吗?