诊断和补全怎么防抖,怎么取消过期请求?
结论
诊断按文档防抖,只对停手后的那个版本计算,发布前核对版本号。补全不靠长时间防抖,每次请求都带取消令牌,新输入到来就停掉上一次,返回前再确认没有被取消。
展开
诊断防抖:
const timers = new Map<string, ReturnType<typeof setTimeout>>();
function schedule(document: vscode.TextDocument) {
const key = document.uri.toString();
const version = document.version;
clearTimeout(timers.get(key));
timers.set(key, setTimeout(() => {
void diagnose(document, version);
}, 300));
}
diagnose 结束时如果 document.version !== version,说明计算期间又改过,丢掉结果,不要 set 到诊断集合。否则会先闪出旧波浪线,再被下一次盖掉。
补全:
- 编辑器自己会在新的补全请求到来时取消上一个令牌
- 你的
provideCompletionItems要把token传进慢操作 - 返回前检查
token.isCancellationRequested - 不要再自己延迟 300 毫秒才返回,列表会显得钝。过滤和触发字符已经由编辑器控制了
请求代数也可以和令牌一起用。代数适合没有令牌的推模型,例如你自己监视文件后推诊断。有令牌的请求就不要再发明第二套难懂的忽略逻辑,两套都检查可以,但取消必须真的停掉 IO。
共享一个定时器给所有文件是错的。A 文件的变更会把 B 文件即将发布的诊断清掉。按 URI 分开。
容易讲错的点
- 防抖等待期间文档可能已关闭。回调里先看文档还在不在。
- 取消后不要
showErrorMessage。 - 语言服务器侧同样处理:过期
didChange版本上的诊断不能publishDiagnostics。