源码索引 / CURRENT IMPLEMENTATION
site/scripts/symbols.mjs
30 行 · 点击行号可定位;函数链接随声明位置更新,不手填行号。
// Declaration navigation for this small source set; not a semantic call graph.
export function symbolsFor(name, code) {
const found = [], scopes = [];
function add(label, line) {
const id = 'fn-' + label.replace(/[^a-z0-9]+/gi, '-').replace(/^-|-$/g, '');
if (found.some(s => s.id === id)) throw new Error(`Duplicate symbol ${name}:${label}`);
found.push({ label, id, line });
}
for (const [index, line] of code.split('\n').entries()) {
let m;
if (name.endsWith('.go')) {
m = line.match(/^func\s+(?:\(\w+\s+\*?(\w+)\)\s*)?(\w+)\s*\(/);
if (m) add(m[1] ? `${m[1]}.${m[2]}` : m[2], index + 1);
} else if (name.endsWith('.py')) {
if (!line.trim() || line.trimStart().startsWith('#')) continue;
const indent = line.match(/^\s*/)[0].replaceAll('\t', ' ').length;
while (scopes.length && scopes.at(-1).indent >= indent) scopes.pop();
m = line.match(/^\s*(?:async\s+)?(def|class)\s+(\w+)/);
if (m) {
const label = [...scopes.map(s => s.name), m[2]].join('.');
add(label, index + 1);
scopes.push({ name: m[2], indent });
}
} else if (name.endsWith('.c')) {
m = line.match(/^(?:EXPORT\([^)]+\)\s*)?(?:static\s+)?(?:PyObject\s*\*|int32_t|uint64_t|void\s*\*|void)\s*(\w+)\s*\([^;]*\)\s*\{/);
if (m) add(m[1], index + 1);
}
}
return found;
}