跳到主要内容

诊断和补全怎么防抖,怎么取消过期请求?

结论​

诊断按文档防抖,只对停手后的那个版本计算,发布前核对版本号。补全不靠长时间防抖,每次请求都带取消令牌,新输入到来就停掉上一次,返回前再确认没有被取消。

展开​

诊断防抖:

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。