VS Code CodeLens异步调用阻塞编辑器 如何基于resolveCodeLens优化
实现方案
核心逻辑
因为你在API返回前无法获知range信息,所以不能完全套用官方“先返回range后填充command”的默认流程,需要搭配主动刷新机制实现,完全避免阻塞主线程:
provideCodeLenses仅做快速返回和请求触发,不等待异步网络调用完成- 后台异步拉取API数据,拿到range和元数据后存入缓存,主动触发CodeLens刷新
- 所有command的构造逻辑全部放到
resolveCodeLens中实现,仅当用户可视区域出现对应CodeLens时才执行填充
具体实现步骤
第一步:调整
首先给Provider类加两个缓存字段:provideCodeLenses逻辑
改造后的// 存储已拉取到的文档对应CodeLens range和元数据 private lensMetaCache = new Map<string, Array<{range: vscode.Range, meta: any}>>(); // 标记正在请求中的文档,避免重复发起调用 private requestingDocs = new Set<string>();provideCodeLenses代码:public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> { if (!vscode.workspace.getConfiguration().get('stenography.codeLensMode')) { return []; } const filename = document.uri.toString(); // 已有缓存直接返回仅带range的CodeLens,不带command if (this.lensMetaCache.has(filename)) { return this.lensMetaCache.get(filename)!.map(item => new vscode.CodeLens(item.range)); } // 没有缓存且没有正在请求,后台触发请求,直接返回空数组 if (!this.requestingDocs.has(filename)) { this.requestingDocs.add(filename); // 异步请求不阻塞返回 vscode.window.withProgress({ location: vscode.ProgressLocation.Window, cancellable: false, title: 'Fetching from Stenography Autopilot' }, async (progress) => { try { progress.report({ increment: 0 }); const language = document.languageId; const data = await fetchStenographyAutopilot(STENOGRAPHY_API_KEY!, document.getText(), language, false); const lensMetas = []; data.code_blocks.forEach((block: any) => { const firstLine = document.lineAt(block.startPosition.row - 1); const textRange = new vscode.Range(firstLine.range.start, firstLine.range.end); lensMetas.push({ range: textRange, meta: block.stenographyResult }); }); // 存入缓存 this.lensMetaCache.set(filename, lensMetas); // 主动触发CodeLens刷新 vscode.languages.refreshCodeLenses(); } catch (e) { console.error(e); } finally { this.requestingDocs.delete(filename); } }); } // 直接返回空,不阻塞 return []; }第二步:实现
resolveCodeLens方法public resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens> { // 遍历缓存找到对应range的元数据,构造command for (const metas of this.lensMetaCache.values()) { const matched = metas.find(item => item.range.isEqual(codeLens.range)); if (matched) { codeLens.command = { title: "<stenography autopilot />", tooltip: matched.meta.pm, command: "stenography.codelensAction", arguments: [matched.meta.pm] }; return codeLens; } } return codeLens; }第三步:补充缓存清理逻辑
监听文档变更、关闭事件,对应删除缓存,避免展示旧数据:// 可以在Provider初始化时注册监听 vscode.workspace.onDidChangeTextDocument(e => { this.lensMetaCache.delete(e.document.uri.toString()); }); vscode.workspace.onDidCloseTextDocument(doc => { this.lensMetaCache.delete(doc.uri.toString()); });
为什么不会阻塞
provideCodeLenses 收到调用后会立刻返回结果,所有网络请求、数据处理逻辑全部在后台异步执行,不会占用VS Code主线程时间。主动刷新机制保证API返回后CodeLens能正常展示,resolveCodeLens的懒加载逻辑也完全符合官方的性能优化建议。
内容的提问来源于stack exchange,提问作者Bram Adams
相关产品推荐
相关产品推荐

