- 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
186 lines
3.9 KiB
Markdown
186 lines
3.9 KiB
Markdown
# Tab 补全行为演示
|
||
|
||
## 🎯 核心洞察:Tab的两个职责
|
||
|
||
### 1️⃣ Tab = "尽可能补全"
|
||
### 2️⃣ Tab Tab = "显示所有选项"
|
||
|
||
---
|
||
|
||
## 📱 实际例子(假设有文件:package.json, package-lock.json)
|
||
|
||
### ✅ 标准终端的智慧
|
||
```bash
|
||
cat p[Tab]
|
||
# 🤔 思考:有 package.json, package-lock.json, public/
|
||
# 💡 决策:补全到公共前缀
|
||
cat package█
|
||
|
||
cat package[Tab]
|
||
# 🤔 思考:还是有歧义
|
||
# 💡 决策:不动(或蜂鸣)
|
||
|
||
cat package[Tab][Tab]
|
||
# 🤔 思考:用户需要看选项了
|
||
# 💡 决策:显示所有
|
||
package.json package-lock.json
|
||
|
||
cat package.j[Tab]
|
||
# 🤔 思考:唯一匹配!
|
||
# 💡 决策:直接补全
|
||
cat package.json█
|
||
```
|
||
|
||
### ❌ 我们现在的问题
|
||
```bash
|
||
cat p[Tab]
|
||
# 😵 立即显示菜单
|
||
▸ package.json
|
||
package-lock.json
|
||
public/
|
||
# 用户:我只是想补全啊,不是要选择!
|
||
```
|
||
|
||
---
|
||
|
||
## 🧠 终端的补全决策树
|
||
|
||
```
|
||
按下 Tab
|
||
↓
|
||
有几个匹配?
|
||
↓
|
||
┌────────┼────────┐
|
||
0个 1个 多个
|
||
↓ ↓ ↓
|
||
蜂鸣 补全完成 有公共前缀?
|
||
↓
|
||
┌────┴────┐
|
||
有 无
|
||
↓ ↓
|
||
补全前缀 是第二次Tab?
|
||
↓
|
||
┌────┴────┐
|
||
是 否
|
||
↓ ↓
|
||
显示菜单 蜂鸣
|
||
```
|
||
|
||
---
|
||
|
||
## 💭 为什么这样设计?
|
||
|
||
### 效率原则
|
||
- **最少按键**: 能补全就补全,不要问
|
||
- **渐进显示**: 只在需要时才显示选项
|
||
- **智能判断**: 根据上下文做最合理的事
|
||
|
||
### 用户心智模型
|
||
```
|
||
Tab = "帮我补全"
|
||
Tab Tab = "我需要看看有什么选项"
|
||
```
|
||
|
||
---
|
||
|
||
## 🔨 我们需要的改动
|
||
|
||
### 当前代码(过于简单)
|
||
```typescript
|
||
if (key.tab) {
|
||
if (suggestions.length > 1) {
|
||
showMenu() // ❌ 太早了!
|
||
}
|
||
}
|
||
```
|
||
|
||
### 应该的代码(智能判断)
|
||
```typescript
|
||
if (key.tab) {
|
||
const now = Date.now()
|
||
const isDoubleTab = (now - lastTabTime) < 300
|
||
|
||
if (suggestions.length === 0) {
|
||
beep()
|
||
} else if (suggestions.length === 1) {
|
||
complete(suggestions[0])
|
||
} else {
|
||
// 多个匹配
|
||
const commonPrefix = findCommonPrefix(suggestions)
|
||
const currentWord = getCurrentWord()
|
||
|
||
if (commonPrefix.length > currentWord.length) {
|
||
// 可以补全到公共前缀
|
||
complete(commonPrefix)
|
||
} else if (isDoubleTab) {
|
||
// 第二次Tab,显示菜单
|
||
showMenu()
|
||
} else {
|
||
// 第一次Tab,但没有可补全的
|
||
beep()
|
||
}
|
||
}
|
||
|
||
lastTabTime = now
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🎮 交互示例
|
||
|
||
### 场景:输入命令参数
|
||
```bash
|
||
# 文件: README.md, README.txt, package.json
|
||
|
||
$ cat R[Tab]
|
||
$ cat README # 补全公共前缀
|
||
|
||
$ cat README[Tab]
|
||
*beep* # 无法继续补全
|
||
|
||
$ cat README[Tab][Tab]
|
||
README.md README.txt # 显示选项
|
||
|
||
$ cat README.m[Tab]
|
||
$ cat README.md # 唯一匹配,完成
|
||
```
|
||
|
||
### 场景:路径导航
|
||
```bash
|
||
$ cd s[Tab]
|
||
$ cd src/ # 唯一匹配,补全+加斜杠
|
||
|
||
$ cd src/[Tab][Tab]
|
||
components/ hooks/ utils/ # 继续显示下级
|
||
|
||
$ cd src/c[Tab]
|
||
$ cd src/components/ # 继续补全
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 影响分析
|
||
|
||
| 操作 | 按键次数(现在) | 按键次数(改进后) | 节省 |
|
||
|------|-----------------|-------------------|------|
|
||
| 输入 package.json | 5次(p+Tab+↓+↓+Enter) | 3次(pa+Tab+.j+Tab) | 40% |
|
||
| 进入 src/components | 4次(s+Tab+Enter+c+Tab) | 2次(s+Tab+c+Tab) | 50% |
|
||
| 选择多个选项之一 | 3次(Tab+↓+Enter) | 4次(Tab+Tab+↓+Enter) | -33% |
|
||
|
||
**平均效率提升:~30%**
|
||
|
||
---
|
||
|
||
## 🚀 结论
|
||
|
||
**一个原则**:Tab应该"尽力而为",而不是"立即放弃"
|
||
|
||
**两个规则**:
|
||
1. 能补全就补全
|
||
2. 不能补全才显示选项(且需要双击)
|
||
|
||
**三个好处**:
|
||
1. 符合用户期望
|
||
2. 减少操作次数
|
||
3. 保持专注流程 |