VSCode 1.67+版本自定义扩展Ctrl+悬停不显示符号定义
问题根因
VSCode 1.67 针对 Ctrl+悬停触发的定义预览逻辑做了不兼容变更,和跳转逻辑的校验规则做了拆分:
- F12转到定义、Ctrl+点击跳转、右键菜单跳转这三类跳转操作,只要
provideDefinition返回包含合法URI和位置的Location对象就能正常运行,不要求range字段合规 - Ctrl+悬停的定义预览新增了强制校验:返回的
Location必须携带非零长度的有效range,如果range未定义、为空、是零宽位置,预览面板会直接判定结果无效,不渲染任何内容。低版本VSCode没有这个校验,所以会正常展示。
当前代码中macroManager.getPositionByMacro(word)返回的Location对象,大概率只指定了目标position,没有填充对应符号的有效range,因此能通过跳转逻辑的校验,无法通过预览逻辑的校验。
修复方法
核心是保证返回的所有Location对象都带有非零长度的合法range,两种实现方式可选:
- 优先读取目标文档对应位置的真实词范围,填充到Location的range字段,这是最规范的实现
- 如果目标文档未打开、暂时无法读取真实词范围,可以手动构造一个以目标position为起点、长度为1的非零range做兜底,同样能通过校验。
修复后参考代码
context.subscriptions.push(vscode.languages.registerDefinitionProvider(['test'], { provideDefinition(doc, position, token) { const wordRange = doc.getWordRangeAtPosition(position); if (!wordRange) return; const word = doc.getText(wordRange); const rst: vscode.Location[] | undefined = macroManager.getPositionByMacro(word); if (!rst?.length) return; return rst.map(loc => { // 已有合法非空range直接返回 if (loc.range && !loc.range.isEmpty) { return loc; } // 尝试读取目标文档获取真实词范围 const targetDoc = vscode.workspace.textDocuments.find( d => d.uri.toString() === loc.uri.toString() ); let validRange: vscode.Range; if (targetDoc) { validRange = targetDoc.getWordRangeAtPosition(loc.range.start) ?? new vscode.Range(loc.range.start, loc.range.start.translate(0, 1)); } else { // 兜底非零range,长度1即可通过校验 validRange = new vscode.Range(loc.range.start, loc.range.start.translate(0, 1)); } return new vscode.Location(loc.uri, validRange); }); } }));
异常排查
如果补全range后功能仍未恢复,检查同语言下是否注册了其他HoverProvider:1.67版本后如果同语言存在HoverProvider返回非空Hover结果,且结果未标记支持定义预览,会覆盖默认的Ctrl+悬停定义预览,这种情况在Hover返回结果中添加canContainDefinitionPreview: true标记即可解决。结合低版本功能正常的现象,该问题出现概率极低。
内容的提问来源于stack exchange,提问作者Oliver
相关产品推荐
相关产品推荐

