// 所有关系来自编译器;这里只按源码位置选择符号和分组显示。 function contains(range, position) { const compare = (left, right) => left.line - right.line || left.character - right.character; return compare(range.start, position) <= 0 && compare(position, range.end) < 0; } // 自动分派用于调用层级;不能抢走显式绑定或 Port 声明的“转到定义”。 const generated = new Set(['verify', 'rest-field', 'rest-model', 'adapter']); function at(index, file, position) { const calls = index.calls.filter(call => !generated.has(call.kind) && call.site.file === file && contains(call.site, position)); const targets = new Set(calls.map(call => call.callee)); if (targets.size) return index.symbols.filter(symbol => targets.has(symbol.id)); return index.symbols.filter(symbol => symbol.declarations.some(location => location.file === file && contains(location, position))); } function relations(index, id, incoming) { const grouped = new Map(); for (const call of index.calls) { if ((incoming ? call.callee : call.caller) !== id) continue; const target = incoming ? call.caller : call.callee; if (!grouped.has(target)) grouped.set(target, []); grouped.get(target).push(call); } return [...grouped].map(([id, calls]) => ({ symbol: index.symbols.find(symbol => symbol.id === id), calls })); } module.exports = { at, relations, contains };