Appearance
功能规格说明:代码补全
创建日期:2026-10-15
概述
代码补全在编辑器光标处以行内灰字(ghost text)给出接下来的代码建议,用户按 Tab 接受。首期同时覆盖 VS Code 扩展与 JetBrains 插件两端,两端共享同一套由 CLI 侧(wave --stdio 子进程)提供的补全请求通道与模型配置;插件端只负责采集光标上下文、发起/取消请求,并把返回文本交给宿主平台的补全 API 渲染。
首期范围包含行内补全与跨文件定义片段,也不做本地模型推理。跨步骤的「下一步编辑」建议(next-edit)是同族的另一项能力,行为另成一份规格(见 下一步编辑建议);两边共用的请求通道与宿主侧跳过条件由本规格定义。跨文件定义片段在 VS Code 与 JetBrains 两端都生效,但采集路径不同:VS Code 侧用解析栈认出「该问哪些符号」、找定义交给宿主语言服务;JetBrains 侧不引入解析栈,改用平台自带的声明解析(见边界情况「跨文件定义片段的范围与预算」)。
补全与对话是两条互不影响的请求通道:补全不经过 agent 的完整对话轮次(无工具、无 system prompt、无 hooks),也不与对话共享既有的全局请求间隔限制(aiService 的 1 次/秒串行间隔)。补全请求的节流、取消与缓存全部由补全自身负责。
用户场景与测试 (必填)
用户故事:编辑停顿后自动出现行内建议(优先级:P1)
作为编码中的用户,我希望在编辑器里敲字停顿一下,光标后就自动出现灰色的续写建议,按 Tab 就能采纳,以便不必离开编辑器、也不必在对话框里描述意图。
为什么是这个优先级:这是补全功能的本体——没有「停顿即建议、Tab 即采纳」这条主链路,其余能力(配置、开关、自适应)都无处依附。插件端此前完全没有补全能力,因此本故事也是该能力的第一个可交付切片。
独立测试:在 VS Code 中打开一个 TypeScript 文件,在一行函数体中间敲入 const user = 后停顿,验证光标后出现灰色建议文本;按 Tab 验证文本写入文档且光标落到建议末尾。在 JetBrains 中用同一个文件重复同样的操作,验证两端行为一致。
验收场景:
- 假设 已配置可用的模型且补全开关为开启,当 用户在编辑器中敲入字符后停顿(停顿时长由自适应防抖决定,基准 200ms、区间 100~1000ms),则 插件向 CLI 发起一次补全请求,并把返回的文本以行内灰字渲染在光标之后。
- 假设 光标后已渲染出建议,当 用户按 Tab 时,则 建议文本被插入文档、幽灵文本消失,且该次插入不进入撤销栈中与用户上一次输入合并的同一步(一次撤销即可撤销这次采纳)。
- 假设 光标后已渲染出建议,当 用户按 Esc 时,则 建议被忽略且本次不被再次请求,直到用户下一次输入或光标移动才可能重新请求。
- 假设 建议为多行,当 用户触发宿主原生的「接受下一个词」操作(VS Code 的
Accept Next Word快捷键)时,则 只接受建议的第一个词或词片段,剩余部分仍以幽灵文本保留。JetBrains 平台是否支持按词部分接受待验证(见边界情况),不支持时该场景在 JetBrains 侧降级为「只有全量接受」。 - 假设 用户的建议已被接受,当 光标位于插入文本末尾且用户继续停顿,则 允许基于新上下文发起下一次补全请求。
- 假设 补全请求已发出但尚未返回,当 用户在文档中继续输入、移动光标、切换编辑器或切换标签页时,则 在途请求被取消(abort),其响应即使迟到也不得渲染。
- 假设 补全请求失败(网络错误、模型不可用、鉴权失效、超时),当 请求返回错误时,则 编辑器不显示任何建议、不弹出错误弹窗、不写入任何状态栏文案,仅静默跳过本次补全;连续失败不得使插件崩溃或阻塞用户编辑。
- 假设 模型返回空文本或纯空白,当 响应到达时,则 不渲染任何幽灵文本。
- 假设 模型返回的文本超过 10 行或超过 1000 字符,当 响应到达时,则 按上限截断后再渲染。
用户故事:跨文件定义片段进入补全上下文(优先级:P2)
作为在业务代码里写调用点的用户,我希望补全能看到我所调用符号在别的文件里的定义、本文件 import 进来的符号,以及光标所在的外层作用域,以便模型不必靠猜来续写参数列表与返回值。
为什么是这个优先级:这是本档改造的全部收益所在——只给文件内上下文时,评测中的补全准确率明显偏低(CrossCodeEval:StarCoder-15B 在 Python 上 code EM 8.82% → 检索到跨文件定义后 15.72%)。但缺失时补全本身仍然可用,按本规格既有的分级(P1 = 可用性前提、P2 = 质量与体验),它属于 P2。它同时是风险点:片段给得越多反而越差(同一评测里 top-5 到 top-20 的准确率单调衰减),所以必须有硬上限,不是「尽量多塞」。
独立测试:在一个含 utils/format.ts(导出 formatDate)的项目里,于另一文件的函数体中敲 formatD 并停顿,验证建议使用了 formatDate 的签名;再把 formatDate 改名后保存并重复,验证片段跟随更新(不回落到磁盘旧内容)。
验收场景:
- 假设 用户在 TS/TSX/JS/Python/Java 文件中,光标前是从其他文件 import 进来的符号,当 补全触发时,则 该符号的定义体被采集进上下文,整体位于光标前缀之前。
- 假设 光标位于某个方法或函数体内,当 补全触发时,则 外层作用域(所在函数的签名、所在类的声明)同在候选片段中。
- 假设 可采集到的定义片段超过上限(默认 3 段或超出字符预算),当 组装上下文时,则 按相关性保留靠前的若干段,且相关性最高的一段排在最后(离光标最近)。
- 假设 片段采集耗时超过 100ms,当 超时发生时,则 放弃片段并以「无片段」的上下文照常发起补全请求,不因采集失败而丢弃本次补全。
- 假设 VS Code 宿主中光标所在语言当前没有语法解析支持(仅 TS/TSX/JS/Python/Java 有),当 补全触发时,则 降级为「只取光标处符号的定义体」,不报错、不提示;JetBrains 侧不依赖 grammar,不触发本降级。
- 假设 定义所在文件的扩展未激活,或定义位置不是
filescheme,当 采集片段时,则 视为该次无片段,不抛错、不影响本次补全。 - 假设 定义所在文件已在编辑器中打开且有未保存修改,当 采集片段时,则 取编辑器内存中的当前内容,不读磁盘旧版本。
- 假设 用户在 JetBrains 宿主中触发补全,当 请求发出时,则 上下文含光标前后文本,以及经
TargetElementUtil解析到的跨文件声明(同样受 3 段上限与 100ms 超时约束);语言插件不提供引用解析时退化为只有光标前后文本,不报错。
用户故事:手动触发补全(优先级:P2)
作为用户,我希望在自动建议没有出现时用一个快捷键主动要一次建议,以便在自动触发被防抖或上下文判定跳过时仍能拿到补全。
为什么是这个优先级:自动触发能覆盖绝大多数场景,手动触发是兜底入口(对应同一份请求链路),不是主链路,故为 P2。
独立测试:在编辑器中把光标停在一行代码中间,按下手动触发快捷键(VS Code / JetBrains 默认均为 Alt+\,可在 IDE 快捷键设置中改绑),验证立即出现建议;连按两次验证不会产生两条并发请求。
验收场景:
- 假设 补全开关为开启,当 用户按下手动触发快捷键时,则 立即发起一次补全请求,不受防抖等待影响。
- 假设 已有一次补全请求在途,当 用户再次按下手动触发快捷键时,则 取消在途请求并发起新的请求(不排队、不并发)。
- 假设 光标后已有建议,当 用户按下手动触发快捷键时,则 不重复请求,保持现有建议不变。
- 假设 补全开关为关闭,当 用户按下手动触发快捷键时,则 不发起任何请求、不显示建议。
用户故事:补全不挤占对话的模型调用(优先级:P1)
作为用户,我希望补全的高频请求不要拖慢、也不要被对话的请求节流卡住,以便我在正常对话的同时也能顺畅敲代码。
为什么是这个优先级:这是补全能否可用的前提——CLI 现有的轻量模型入口共用一个进程级 1 次/秒的串行请求间隔(aiService 的 MIN_INTERVAL = 1000),若补全复用该通道,行内补全的请求速率会被强制降到 1 次/秒并与对话轮次互相排队,两端都无法接受。这条约束决定补全必须是一条独立通道。
独立测试:在对话正在流式输出的同时连续敲入多段代码并停顿,验证补全仍按自身节奏返回、对话流式输出不出现额外停顿;反向验证补全请求不会被对话的请求节奏压到 1 次/秒。
验收场景:
- 假设 对话正在执行一轮(含工具调用),当 用户触发补全时,则 补全请求独立发起并返回,不与对话轮次互相排队。
- 假设 短时间内连续产生多次补全请求,当 请求发出时,则 同一时刻只允许一个补全请求在途,新请求取消旧请求(在途取消)+ 防抖合并,请求速率由补全自身的防抖与缓存决定,不套用对话的 1 次/秒间隔。
- 假设 补全请求在途且用户继续输入,当 用户敲击下一个字符时,则 立即取消在途请求,不等待响应、不产生第二条并发请求。
- 假设 一次补全请求被取消,当 其响应仍随后到达时,则 该响应被丢弃,不影响当前编辑状态。
- 假设 补全模型调用失败,当 错误发生时,则 错误只影响该次补全,不得中断、挂起或改变对话侧的任何状态。
用户故事:为补全单独配置模型(优先级:P1)
作为用户,我希望能为代码补全单独指定一个模型,以便用低延迟的补全专用模型(而不是对话用的大模型)换取更快的建议。
为什么是这个优先级:模型选择直接决定补全延迟与质量;思考型或大模型会让建议慢到不可用,因此「能选一个专用补全模型」是可用性的组成部分,而非可选优化。
独立测试:在设置中把补全模型指定为模型 A、对话模型指定为模型 B,触发补全并抓取请求,验证补全请求打到模型 A、对话请求打到模型 B;再把补全模型清空,验证补全回退到快速模型(fastModel)且仍能返回建议。
验收场景:
- 假设 用户在设置中指定了补全模型,当 触发补全时,则 请求使用该模型;未指定补全模型时回退到已配置的快速模型(fastModel)。
- 假设 补全模型候选列表已存在(与对话模型候选同源,即 CLI 已配置的模型集合),当 用户在设置页「全局设置 → 基础设置」打开「补全模型」下拉时,则 展示与对话模型一致的候选清单与当前值;该键未设置(settings.json 无此键)时下拉首项为显式项「未设置(跟随快速模型)」并选中(下拉没有 placeholder 语义),语义与语言下拉的「未设置(默认:中文)」一致(该行只在提供补全的宿主上渲染,见边界情况「补全设置行的宿主范围」)。
- 假设 用户修改了补全模型或补全开关,当 用户在设置页「全局设置 → 基础设置」改动它(开关拨动即写、下拉选中即写,设置页没有「保存」按钮,见 agent-config.md「设置页控件的保存时机」故事),则 该键写入 CLI 侧用户级
~/.wave/settings.json的顶层键(与对话模型等现有设置项同一份真源,经 SDK 实时重载在下一轮生效),插件端不持有第二份模型配置;下一次补全请求即按新值生效,无需重启 IDE。 - 假设 补全模型名在配置中已失效(模型被移除或改名),当 触发补全时,则 该次补全静默失败(不弹错、不影响对话),设置页对失效值给出可读提示:下拉仍保留并显示该值(不静默回落到别的模型名),并在行内说明「当前值不在已配置的模型列表中」。
- 假设 用户尚未登录或尚未配置任何可用模型,当 用户在编辑器中敲字停顿,则 不发起补全请求、不显示任何建议(不产生鉴权失败噪音)。
用户故事:补全开关与生效范围(优先级:P2)
作为用户,我希望能够整体关闭补全、或只在某些语言里启用,并且在大文件、只读文件等不该打扰的场景里自动静默,以便补全不侵入不适合它的场合。
为什么是这个优先级:默认行为已可用,开关与范围控制属于让不同偏好的用户都能接受该能力的配套项,故为 P2。
独立测试:在设置中关闭补全开关,验证任何编辑停顿都不产生请求、不显示建议;重新开启后恢复;把某语言加入禁用列表,验证该语言文件中不再出现建议。
验收场景:
- 假设 补全开关为关闭(设置页「全局设置 → 基础设置」的「代码补全」开关,缺省为开),当 用户在编辑器中任意输入与停顿,则 不发起任何补全请求、不显示建议(开关在 SDK 侧判定,关闭时连一次模型请求都不发出);该开关所在行的宿主范围见边界情况「补全设置行的宿主范围」。
- 假设 补全开关为开启(默认开启),当 用户在多语言混排的项目中编辑,则 默认对所有文本类语言生效。
- 假设 用户把某语言加入了补全禁用列表,当 用户在该语言文件中编辑时,则 不发起补全请求;从列表移除后恢复。
- 假设 当前文档是只读文件、未保存的新建文件(untitled)、设置页等虚拟文档、diff 视图或笔记本单元格,当 用户在其中暂停输入时,则 不发起补全请求、不显示建议。
- 假设 当前文档体积超过阈值(默认 1MB 或 20000 行),当 用户暂停输入时,则 跳过补全,不发起请求。
- 假设 编辑器中有非空选区或多光标,当 用户暂停输入时,则 不在选区处渲染建议(仅光标位于单个空选区处才触发)。
- 假设 用户正在使用输入法组合输入(如拼音未上屏),当 组合过程进行中时,则 不发起补全请求,待组合提交后再按停顿判断。
用户故事:建议的响应迟滞自适应与缓存(优先级:P2)
作为用户,我希望补全在模型响应慢时不要频繁打扰、在响应快时尽快给出建议,并且重复的上下文不要重复请求,以便补全体感跟得上打字节奏。
为什么是这个优先级:这是补全的体验调优层——缺失时功能仍可用(只是体感差),故为 P2;但它决定了补全在真实网络条件下的可用程度。
独立测试:在人为放慢模型响应的环境下连续输入,验证等待时间随最近响应耗时延长(上限 1000ms);在响应正常时验证等待时间回落到基准附近。在同一位置反复输入删除相同的代码(回到相同前后缀),验证第二次不产生新请求而直接复用缓存结果。
验收场景:
- 假设 最近的补全响应耗时明显高于基准(如接近 1000ms),当 用户继续输入并停顿,则 防抖等待时间相应延长(上限 1000ms),减少无效请求。
- 假设 最近的补全响应很快,当 用户继续输入并停顿,则 防抖等待回落至基准附近(下限 100ms)。
- 假设 用户回到此前请求过的相同上下文(同一文档、同一文档版本、相同前缀与后缀),当 用户再次停顿,则 直接复用缓存中的建议而不发起新请求。
- 假设 缓存条目超过容量(默认 100 条)或超过存活时间(默认 5 分钟),当 新条目写入时,则 按最近最少使用与过期策略淘汰旧条目。
- 假设 补全请求正在等待响应,当 用户敲入新字符时,则 请求立即取消而非等待完成(避免用旧上下文的结果覆盖新位置)。
- 假设 补全请求在途,当 请求进行中,则 编辑器不显示任何加载指示(不闪烁、不显示进度),用户输入不被打断。
边界情况
- 补全请求与对话共享同一 CLI 子进程,但不得共享队列。 补全不经过
aiManager的对话轮次(无工具、无 system prompt、无 hooks),也不走aiService.callAgent的全局 1 次/秒请求间隔;补全在 CLI 侧有独立的调用路径与自己的节流策略。 - 模型配置真源在 CLI 侧。 插件端不得自建第二份模型配置(插件端既有配置服务明确不负责模型选择);补全模型与开关走用户偏好链路落盘到 CLI 侧用户级
~/.wave/settings.json的顶层键completionModel/completionEnabled——设置页「全局设置 → 基础设置」的两行都经既有 webview→host 命令updateConfiguration提交——「代码补全」开关拨动即写盘并即时生效(载荷只含completionEnabled,不依赖底部「保存」,失败回滚 + toast,见 agent-config.md「设置页控件的保存时机」故事),「补全模型」下拉选中即写盘(同属updateConfiguration单键载荷,设置页已无「保存」按钮),宿主转成 stdio RPCupdateUserSettings(省略键 = 不改该键的 diff 语义照旧),插件端只做读写转发、不持有第二份模型配置;候选清单与对话模型同源,经getConfiguredModels拉取(设置页外壳在插件端是独立 webview,该命令必须同时注册在设置页路由,scripts/audit-webview-commands.mjs会拦漏注册)。补全请求本身走新增的 stdio RPC 方法getCompletion(参数:prefix/suffix/language/filePath;响应:{ text, model },text为空串表示无建议),会话级调用,同一会话同一时刻只保留一个在途请求——新请求到达时旧请求被中止,因此迟到的响应不会渲染。 - 补全设置行的宿主范围:只由提供补全的宿主渲染。 「代码补全」开关与「补全模型」下拉只出现在提供行内补全的宿主(VS Code / JetBrains)的设置页里;桌面端设置页不展示这两行——桌面端没有代码编辑器,补全在那里从不触发,把开关摆在那里等于承诺一个不会发生的功能(远端网页等无编辑器宿主同理),见 desktop-settings.md 场景 3。设置页在插件端与桌面端共用同一套实现,因此该门禁必须按宿主能力位判定(有没有编辑器,而不是「宿主叫什么名字」),不得依赖「桌面端不渲染设置页」这类隐含前提。CLI 侧的键(顶层
completionModel/completionEnabled)、解析链与读写 RPC 不受影响:宿主只是不渲染这两行,键与语义照旧(命令行/手改 settings.json 仍然可配)。 - 补全开关在 SDK 侧生效。 开关关闭时
getCompletion在发起模型调用前直接返回空文本,不消耗一次模型请求。开关与补全模型读 settings.json 的顶层键completionModel/completionEnabled,解析优先级为AgentOptions> settings.json > 环境变量WAVE_COMPLETION_MODEL>fastModel;completionEnabled缺省为开,设置页的开关不做占位态(它的真实默认就是「开」,口径与自动记忆开关一致),未设置且未被拨动时不写该键。补全模型未设置时回退快速模型,不回退对话主模型;快速模型自身的取值链与model对称(AgentOptions> settings.jsonfastModel>env.WAVE_FAST_MODEL)。 - 3 秒请求超时。 宿主侧对单个补全请求设 3 秒上限,超时即按“无建议”处理并取消在途请求——超过这个时间的建议已无价值,且持续占用在途通道。跨文件片段的采集(100ms 上限)发生在这 3 秒之内,不额外延长总超时。
- 补全通道不得把 agent 运行时拖进宿主包。 宿主只能从
wave-agent-sdk/host导入,且该入口的依赖闭包必须零第三方依赖、体积受限(scripts/check-sdk-surface.mjs);补全的请求实现只能放在 CLI/SDK 侧,插件端拿不到也不得拿到底层模型客户端。跨文件片段所需的语法解析栈(tree-sitter 运行时与各语言 grammar)属于第三方依赖,只能存在于采集侧宿主包(VS Code 扩展)内,不得进入wave-agent-sdk的/host入口闭包。 - 上下文范围: 请求内容默认包含光标前缀最多 150 行与光标后缀最多 30 行,并对前缀+后缀设总字符上限(默认 12000 字符),超出时从离光标最远处开始截断;跨文件定义片段另计一份预算,不占用这 12000 字符。不引入代码库向量索引,也不做仓库级全文检索。
- 跨文件定义片段的范围与预算: 只采集三类,且都不做检索:① 光标前符号的定义体;② 本文件 import 语句导入的符号的定义体;③ 光标所在的外层作用域(函数签名、类声明)。定义落在当前文件的候选一律跳过——它的正文通常已在光标前缀窗口内,重复只会挤占预算。不采集「最近打开的文件」——片段数量超过最优区间后准确率反而下降,因此上限是硬上限(默认 3 段)。
- 采集在插件侧完成,两端机制不同:
- VS Code:用 tree-sitter 解析当前文件,得到光标处的 AST 作用域链与 import 语句里的符号名;再逐个经宿主语言服务(VS Code
vscode.executeDefinitionProvider)换成定义位置并读取定义体。tree-sitter 只负责「该问哪些符号」,找定义永远走宿主语言服务——宿主语言服务没有「给我这个文件、还我 import 了哪些名字」的接口,而正则会被注释里的 import、字符串中的同形文本、跨行 import、import type与as重命名骗到,这是引入解析栈的唯一理由。 - JetBrains:不引入解析栈,改用平台语言无关 API
TargetElementUtil.findTargetElement(ELEMENT_NAME_ACCEPTED | REFERENCED_ELEMENT_ACCEPTED)——它由各语言插件实现,对 IDE 认得的所有语言都成立。候选来源是「光标处 ±1 字符」加「以import/from/using/use/require开头的行里的标识符」(正则粗筛负责认出 import 行,符号本身不靠正则判定):落在引用上的候选解析出声明,落在关键字或字符串里的候选自然返回null,因此不需要每语言写 import 语法,也不需要区分语言。该调用不可取消,整批放进一次ReadAction并按 100ms 硬超时兜底。
- VS Code:用 tree-sitter 解析当前文件,得到光标处的 AST 作用域链与 import 语句里的符号名;再逐个经宿主语言服务(VS Code
- 语言覆盖:VS Code 侧带 TS/TSX/JS/Python/Java 五种 grammar(Python 与 Java 各增一个 grammar 文件,包体约 +120 KB);其他语言没有解析支持时降级为「只取光标处符号的定义体」。作用域节点类型与 import 语法按语言分别登记,新增语言需要同时补 grammar 文件、作用域节点表和 import 查询——三者缺一都会静默退化成「找不到符号」。JetBrains 侧不依赖 grammar,覆盖 IDE 本身支持的全部语言,但它采到的片段质量取决于该语言的插件是否实现了引用解析。
- 预算与超时:采集整体 100ms 硬超时,超时即按无片段处理、请求照常发出;插件侧只做「取、读、截、限流 ≤3 段」并只按字符预算截断,token 预算、去重、路径注释头与最终拼装都在 CLI 侧。
- 新鲜度:片段原文取编辑器内存中的当前文档内容(含未保存修改),不读磁盘缓存——陈旧片段不只是噪音,会把模型引向过时的 API。
- 顺序:额外片段整体前置到光标前缀之前,其中相关性最高的一段放最后(离光标最近)。
- 协议:插件向 CLI 传可选字段
snippets;undefined表示该宿主没有这项能力,[]表示尝试采集但没拿到,两者对 CLI 都是「无片段」。
- 采集在插件侧完成,两端机制不同:
- 隐私与数据外发: 前缀/后缀内容、以及被采集到的那几段跨文件定义体(含文件路径注释头)会随补全请求发送到用户已配置的模型服务(与对话同一网关与凭据);不额外上传整个文件或仓库,也不做代码库级上传;首期不支持本地推理。
- JetBrains 平台差异(已对照 IC-2024.2.6 平台源码验证): JetBrains 侧使用平台行内补全扩展点(
<inline.completion.provider>/InlineCompletionProvider+InlineCompletionSingleSuggestion.build { emit(InlineCompletionGrayTextElement(text)) }),而非自绘 Inlay;防抖、取消与多光标过滤由平台负责,插件侧不再叠加自己的防抖,只做 3 秒超时与返回前的光标位置校验。跨文件定义片段在 JetBrains 同样生效,但走的是另一条实现路径:JVM 加载不了 wasm grammar,而复制一份解析栈进插件或 CLI 又会让所有宿主为单一能力增重(wasm grammar 合计约 6.2 MB),因此改用平台自带的TargetElementUtil——它把「这个位置指向哪个声明」这件事交给各语言的插件,插件侧只负责挑候选位置。这条是开源同行的既有做法(Tabby 的DefaultLanguageSupportProvider用同一个 API 提供声明位置),Continue 则因为在该钩子上留了NotImplementedError而在 JetBrains 侧没有跨文件能力。代价是精度依赖语言插件:引用解析做得差的文件类型,片段会退化成「采不到」。 - 只读与不可写文档按 scheme 判定: VS Code 侧用
workspace.fs.isWritableFileSystem(scheme)(仅明确返回 false 时跳过)判断可写性,JetBrains 侧用Document.isWritable;未命名文档、虚拟文件(非filescheme)、内联 diff / 多差异编辑器中的探针文档一律跳过。 - 手动触发复用平台内置动作: 两端均绑定平台自带的行内建议触发命令(VS Code
editor.action.inlineSuggest.trigger,默认快捷键Alt+\),不新增插件命令;手动触发跳过防抖等待,直接进入请求。 - 建议与实际文本的漂移: 响应返回时若文档版本已变化(用户已继续编辑),该响应必须丢弃,不得按旧位置插入。
- 多标签页与多窗口: 同一 IDE 内多个编辑器同时打开时,只有获得聚焦的编辑器可持有在途补全请求;切换编辑器取消在途请求。
- 与内联差异/冲突标记共存: 文档处于合并冲突、内联 diff(如 blame/commit 视图)等特殊渲染状态时跳过补全。
- 建议不得触发其他副作用: 渲染与取消建议都不写入文档、不入撤销栈、不改变光标位置;只有接受(Tab)才写入文档。
- 补全依赖会话已建立: 补全模型与网关凭据都由 CLI 侧的会话解析,插件端在首个会话(侧边栏/标签页/窗口任一)建立前无法回答补全请求,此时按“无建议”静默处理;VS Code 侧因此不声明补全相关的激活事件——扩展未激活(未打开过 Wave 面板)时不会显示补全,避免为补全在每次 VS Code 启动时都付一次 CLI 进程启动成本。
假设
- 用户在 IDE 中已登录 wave,且 CLI 侧已配置至少一个可用模型(或已配置快速模型作为回退)。
- 插件与 CLI 的 stdio 连接已建立(见 Stdio 传输层);补全复用该连接,不额外 spawn 进程。
- 补全的请求链路与参数默认值两端共享,差异仅限宿主平台的渲染、快捷键接入与上下文采集(见 插件端);跨文件片段由两端各自采集(路径不同,见边界情况「跨文件定义片段的范围与预算」),CLI/SDK 侧只消费可选字段
snippets。 - 补全的响应延迟以模型服务实际能力为上限;规格中的数值(防抖基准/区间、上下文行数、缓存容量与存活时间、文件体积阈值、建议行数上限)均为默认值,可在实现与调优阶段调整,调整后需同步更新本规格。