跳到主要内容

Continue — 自动补全与 Next Edit 预测

前四章讲的是「对话式 agent」。本章讲 IDE 里另一条产品线:你打字时的灰色补全(autocomplete)和「猜你下一处要改哪」(Next Edit)。它们和 agent 循环是平行的两套机制。

1. 这一章解决的小问题

agent 循环是「你说一句、它干一串」。但在 IDE 里写代码时,你想要的是更轻、更即时的辅助:

  • autocomplete: 打到一半,自动给灰色「幽灵文本」建议,Tab 接受。
  • Next Edit: 你刚改完一处,它预测你下一处多半要改哪、改成什么,提前给你。

这两套都对延迟极敏感(慢一点就烦人),所以工程重点在「尽量别触发、触发了尽量快、能缓存就缓存」。

2. autocomplete:一条带闸门的管线

它要解决的小问题: 每次按键都可能触发补全,但大多数按键根本不该出补全(在字符串中间、刚补全过、模型没东西可补…)。所以这是一条层层把关 + 缓存的管线。

怎么读这张图:从上到下,任意一关不通过就直接返回 undefined(不出补全);命中缓存则跳过模型。

provideInlineCompletionItems(input)

├─ 准备 LLM(没有就退出)
├─ 敏感文件? → 退出
├─ 防抖 debounce:太快连打就放弃这次
├─ shouldPrefilter:不该补的情形 → 退出
├─ 取上下文片段 getAllSnippetsWithoutRace(并发拿 LSP 定义/邻近代码)
├─ renderPromptWithTokenLimit:在 token 预算内拼 prompt(prefix/suffix)
├─ 缓存命中? ──是──► 用缓存补全
│ 否
│ ▼
├─ 流式补全 streamCompletionWithFilters(+ 多行判定)
├─ postprocessCompletion:后处理(去重/截断/括号匹配)
└─ 写缓存 + 返回 outcome

真实实现: 入口 CompletionProvider.provideInlineCompletionItems()(core/autocomplete/CompletionProvider.ts:150)。各关对应:

关卡干什么符号/位置
防抖太快连打就 debounce 掉debouncer.delayAndShouldDebounce (CompletionProvider.ts:178)
敏感文件ignore 命中则不补isSecurityConcern (CompletionProvider.ts:170)
预过滤判断「该不该补」shouldPrefilter (CompletionProvider.ts:196)
取片段并发拿 LSP 定义 + 邻近/导入片段getAllSnippetsWithoutRace (CompletionProvider.ts:201)
拼 prompt在 token 上限内组 prefix/suffixrenderPromptWithTokenLimit (CompletionProvider.ts:210)
缓存LRU 缓存按 prunedPrefix 取/存AutocompleteLruCache (CompletionProvider.ts:220-227292-296)
流式补全边出边过滤completionStreamer.streamCompletionWithFilters (CompletionProvider.ts:232)
后处理去重/截断/括号匹配postprocessCompletion (CompletionProvider.ts:253)

几个工程细节:

  • 缓存键是 prunedPrefix(剪过的前缀),命中直接复用,cacheHit=true(CompletionProvider.ts:220-227);未命中且开了缓存才异步写回(CompletionProvider.ts:292-296)。
  • 多行判定:shouldCompleteMultiline(helper) 决定补一行还是多行(CompletionProvider.ts:229-230)。
  • 错误静默:Ollama 的「operation was aborted」等可预期错误进 ERRORS_TO_IGNORE,不打扰用户(CompletionProvider.ts:27-31onError CompletionProvider.ts:99)。
  • 接受时反馈括号匹配:用户接受补全后,bracketMatchingService.handleAcceptedCompletion 学习括号配对(CompletionProvider.ts:119-128)。

3. Next Edit:预测「下一处改哪」

它要解决的小问题: autocomplete 补的是光标当前位置往后的字符。但很多时候你的下一个动作是去别处改一行(比如改了函数签名,接着要改调用处)。Next Edit 想直接预测这个。

它怎么转(据 core/nextEdit/README.md 与代码):

触发(和 autocomplete 同一时机,经 VS Code inline completion provider)

├─ 捕获当前光标位置
├─ 定义「可编辑区」= 光标上下各 ±5 行
├─ 捕获用户最近一次编辑(作为 unified diff)
├─ 发给模型 → 返回「新的可编辑区」内容(即预测后的样子)
├─ 用 SVG 装饰把新区域画出来
└─ Tab 接受(旧区域替换为新区域,光标移到最后一处变化)/ Esc 拒绝

真实实现: 提供方是 NextEditProvider(core/nextEdit/NextEditProvider.ts),周边有 NextEditEditableRegionCalculator(算可编辑区)、DocumentHistoryTracker(追踪最近编辑)、NextEditPrefetchQueue(预取队列)、NextEditProviderFactory。README(core/nextEdit/README.md)坦白当前状态:用户可在 autocomplete 与 next edit 间切换;它复用 autocomplete 的触发路径;并明说「最近编辑的 diff 捕获目前有 bug」、JetBrains 集成待做。

和 autocomplete 的关系:

维度autocompleteNext Edit
补什么光标处往后的字符(幽灵文本)一整块「可编辑区」的预测新内容
呈现灰色 inline 文本SVG 装饰显示新区域
触发IDE inline completion同一入口(复用 autocomplete 触发)
成熟度主力、稳定较新,README 自陈有待完善

4. 巧妙之处

  • 「尽量不触发」是第一性能策略。 autocomplete 把防抖、敏感文件、预过滤放在最前,大量按键根本不进模型——延迟和成本都省在这。
  • 片段获取并发且防竞态。 getAllSnippetsWithoutRace(名字即设计):并发拿 LSP 定义和邻近代码,但避免慢的那个拖死整次补全。
  • prompt 受 token 预算约束地拼装。 renderPromptWithTokenLimit 在有限窗口里塞 prefix/suffix/片段,而不是无脑塞满。
  • Next Edit 复用 autocomplete 的触发与过滤管线,避免重造一套时机判断(也因此继承了它的一些过滤行为,见 README 讨论)。

5. 边界与局限

  • Next Edit 按 README 自陈仍在打磨:最近编辑的 diff 捕获有 bug、触发时机依赖 autocomplete 的过滤逻辑、JetBrains 未集成。把它当「实验特性」看更准确。
  • 两套机制都强依赖低延迟;模型/网络慢时体验会明显劣化,这也是为什么有这么多前置闸门和缓存。
  • 这两套属 IDE 侧(core/autocompletecore/nextEdit),CLI agent 走的是 [01 章] 的对话循环,二者不共用补全管线。

6. 横向对比

autocomplete 的「防抖 + 预过滤 + 片段检索 + 受限 prompt + LRU 缓存 + 后处理」是行业内联补全(如各类 Copilot 类工具)的标准管线;Continue 把它做成了 core/ 里可被各 IDE 壳复用的一套。Next Edit 这种「预测下一处编辑位置」是较新的方向,本质上是把「补全」从「补光标处」推广到「补任意处」。

7. 代码地图

主题文件符号名
补全入口core/autocomplete/CompletionProvider.tsCompletionProvider.provideInlineCompletionItems
防抖core/autocomplete/util/AutocompleteDebouncer.tsAutocompleteDebouncer
预过滤core/autocomplete/prefiltering/index.tsshouldPrefilter
片段检索core/autocomplete/snippets/index.tsgetAllSnippetsWithoutRace
prompt 拼装core/autocomplete/templating/index.tsrenderPromptWithTokenLimit
流式补全core/autocomplete/generation/CompletionStreamer.tsCompletionStreamer.streamCompletionWithFilters
后处理core/autocomplete/postprocessing/index.tspostprocessCompletion
LRU 缓存core/autocomplete/util/AutocompleteLruCache.tsAutocompleteLruCache
Next Edit 提供方core/nextEdit/NextEditProvider.tsNextEditProvider
可编辑区计算core/nextEdit/NextEditEditableRegionCalculator.tsNextEditEditableRegionCalculator
编辑历史追踪core/nextEdit/DocumentHistoryTracker.tsDocumentHistoryTracker