Closed Ricbet closed 2 months ago
多行补全示例(仅新增)
智能重写示例(仅修改)
智能重写示例(有删除)
假定原始文本是:
'merge-conflicts.o.non-conflicts.from.left': 'target branch: {0} places',
'merge-conflicts.non-conflicts.from.right': 'source branch: {0} places',
'merge-conflicts.non-conflicts.from.base': 'base branch: {0} places',
补全的文本是:
'merge-conflicts.opensumi.non-conflicts.from.left': 'target branch: {0} places',
'merge-conflicts.opensumi.non-conflicts.from.right': 'source branch: {0} places',
'merge-conflicts.opensumi.non-conflicts.from.base': 'base branch: {0} places',
对于 仅新增 的情况,从多行补全的最终渲染效果来说,我们应该按 字符 级去进行 diff 差异计算。 因为从补全的文本结果来看,有一部分是编辑器里就有的原始字符,这部分你不能去覆盖他,只能显示编辑器里没有的字符。
我们把原始文本和补全文本都按 ""
去做 split
,然后一个个去比较字符,并构造数据结构
我们假设定义的数据结构是
interface ICharChange {
// 下标
index: number,
/**
* 类型
* unchanged: 表示这个字符(或词)与原始文本相等
*/
mode: 'added' | 'removed' | 'unchanged',
// 文本
content: string
}
将 diff 计算过程中的结果构造出以上数据。 diff 的算法有很多种,我们可以采用窗口滑动算法来计算他们的文本差异。 当计算出来后,利用 monaco 的 decoration api 则可以渲染阴影部分的字符
对于 有删除 的情况比较特殊,因为没法通过 阴影字符 去表达已删除。所以只能打开智能重写的预览窗口。
从智能重写的预览窗口来看,差异的色块部分并不是按 字符 去渲染的,而是以 词 为单位。 我们可以用正则表达式去切 词 ,然后还是与上述一样,进行差异 diff 计算并构造数据结构。
右侧的预览窗口我们可以用 monaco 的 content widget api 去渲染,然后在里面创建一个小的 monaco editor 去展示
智能重写 PR: https://github.com/opensumi/core/pull/3962
[x] 只有新增的情况
[x] 全删除的情况
[x] 既有新增又有删除的情况
[x] tab 采纳操作
[x] 优化触发时机
背景
https://docs.cursor.com/tab/overview
cursor 的 copilot++ 的代码补全功能不仅可以行补全和块补全,还能一次性进行多行补全(在光标的上下 xx 范围内提示补全)
类似这种效果
除此之外还有“智能重写”的能力,与多行补全不同的是,智能重写会将光标上下 xx 范围内对代码进行重写,然后以悬浮窗口的形式提供预览
两者通常是一起搭配出现的
https://github.com/user-attachments/assets/ffaea3c5-bcbc-48fb-b4e0-b84511d0810a
他会以当前活动光标的位置信息来决定是要显示多行补全还是显示智能重写的预览。
与传统的智能补全有什么不同?
传统的智能补全解决的是用户的 “写新代码” 场景。 也就是说,当你要开始写一段新代码了,你回个车或者先写个注释,亦或者先输入一点代码,AI 会自动帮你 “续写” 后面的内容,AI 会认为你光标之前的代码就是正确的。
但在真实的编码工作中,并不全是 “写新代码”,大多数是 改写代码。 所以 多行补全 和 智能重写 解决的就是这个问题,他能对光标所在的局部区域进行 改写 和 新增 的推荐,与传统的 智能补全 相融合,让 AI 能参与更多的 编码任务
实现思路拆解
从体验的效果以及源码来看,通常是会在编码完一定时间之后,触发一次补全。 与业界的补全方案不同的是,cursor 会有某个策略来决定补全的范围,而不是简单的只是在光标位置后面进行补全。
然后 cursor 会将补全结果与补全范围内的代码进行 diff 计算,根据计算的结果可能会出现以下 3 种情况
模块 API 实现
待补充,会结合 https://github.com/opensumi/core/issues/3853 一起来实现