Vendored WhatsApp/tree-sitter-erlang 0.19 (the ELP grammar, ABI 14) with an Erlang-shaped extractor: multi-clause/multi-arity functions merged into one symbol, -spec signatures, records with fields, -type/-opaque aliases, -define macros, -include/-include_lib file edges, and -export-driven visibility. Modules wrap in a namespace so remote mod:fn(...) calls resolve through the existing qualified-name matcher as mod::fn with zero resolver changes. -behaviour declarations link to the behaviour module — gated to namespace targets only (bare-name fallthrough linked -behaviour(supervisor) to an unrelated macro constant on emqx). OTP indirection with static targets is followed: spawn/apply/proc_lib/timer/rpc MFA-argument callees, and gen_server:call/cast(?MODULE | ?SERVER) to the module's own handle_call/handle_cast. Var-module dispatch and message sends stay deliberately unlinked. codegraph_explore also normalizes Erlang-native query spelling (mod:fn/3, init/2) so named symbols resolve as typed. Benchmarked on cowboy (189 files), ejabberd (414), emqx (2,447): extraction PASS on all three; with-codegraph arms reached 2/2/0 file Reads vs 10/5+/19 without, fastest on the largest repo. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
63e1b5a23a
commit
6511722250
+37
-4
@@ -95,6 +95,36 @@ function lastQualifierPart(symbol: string): string {
|
||||
return parts[parts.length - 1] ?? symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize Erlang-native symbol spellings in an explore query into the shapes
|
||||
* the rest of the pipeline already understands. Agents working Erlang code
|
||||
* name symbols the way the language spells them — `mod:fn/3`, `init/2` — and
|
||||
* those tokens previously died in both consumers: the flow-builder's token
|
||||
* filter rejects `:` and `/arity` outright, and the search-side field parser
|
||||
* eats `mod:fn` as an unknown `field:value`. Measured on cowboy: the agent
|
||||
* named `cowboy_stream_h:request_process/3` in two queries, got no body back
|
||||
* either time, and fell back to Read.
|
||||
*
|
||||
* - `fn/3` → `fn` (arity tail after an identifier; a path segment like
|
||||
* `src/2fa` doesn't match because the tail must be all digits)
|
||||
* - `mod:fn` → `mod.fn` (exactly one colon between identifiers, so it rides
|
||||
* the existing Class.method qualified handling; `::`, URLs, drive letters,
|
||||
* and times don't match, and the query language's own field prefixes —
|
||||
* kind:/lang:/language:/path:/name: — are left alone)
|
||||
*
|
||||
* Safe cross-language: Lua's `t:m` spelling maps to the same `t.m` its
|
||||
* qualified names use, and no other supported spelling contains a bare
|
||||
* single-colon identifier pair.
|
||||
*/
|
||||
export function normalizeQuerySpelling(query: string): string {
|
||||
return query
|
||||
.replace(/\b([A-Za-z_][\w@]*)\/(\d{1,3})(?=$|[\s,()[\]/])/g, '$1')
|
||||
.replace(
|
||||
/(^|[\s,()[\]])(?!(?:kind|lang|language|path|name):)([a-z_][\w@]*):([A-Za-z_][\w@]*)(?=$|[\s,()[\]])/g,
|
||||
'$1$2.$3'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the recommended number of codegraph_explore calls based on project size.
|
||||
* Larger codebases need more exploration calls to cover their surface area,
|
||||
@@ -1854,7 +1884,7 @@ export class ToolHandler {
|
||||
// names (Class.method / Class::method) — the agent's most precise input,
|
||||
// resolved exactly by findAllSymbols. (The old strip mangled Class.method
|
||||
// into Class, throwing the method away.)
|
||||
const FILE_EXT = /\.(?:java|kt|kts|ts|tsx|js|jsx|mjs|cjs|cs|py|go|rb|php|swift|rs|cpp|cc|cxx|c|h|hpp|scala|lua|dart|vue|svelte|astro)$/i;
|
||||
const FILE_EXT = /\.(?:java|kt|kts|ts|tsx|js|jsx|mjs|cjs|cs|py|go|rb|php|swift|rs|cpp|cc|cxx|c|h|hpp|scala|lua|dart|vue|svelte|astro|erl|hrl)$/i;
|
||||
const tokens = [...new Set(
|
||||
query.split(/[\s,()[\]]+/)
|
||||
.map((t) => t.replace(FILE_EXT, '').trim())
|
||||
@@ -2457,8 +2487,11 @@ export class ToolHandler {
|
||||
* tax on small projects while earning its keep on large ones.
|
||||
*/
|
||||
private async handleExplore(args: Record<string, unknown>): Promise<ToolResult> {
|
||||
const query = this.validateString(args.query, 'query');
|
||||
if (typeof query !== 'string') return query;
|
||||
const rawQuery = this.validateString(args.query, 'query');
|
||||
if (typeof rawQuery !== 'string') return rawQuery;
|
||||
// One normalization point so the flow-builder, relevance search, and
|
||||
// ranking all see the same canonical spelling (Erlang `mod:fn/arity`).
|
||||
const query = normalizeQuerySpelling(rawQuery);
|
||||
|
||||
const cg = this.getCodeGraph(args.projectPath as string | undefined);
|
||||
const projectRoot = cg.getProjectRoot();
|
||||
@@ -2539,7 +2572,7 @@ export class ToolHandler {
|
||||
// overloads (the query also named the type) all earn it. (#1064)
|
||||
const tierSeedIds = new Set<string>();
|
||||
{
|
||||
const FILE_EXT = /\.(?:java|kt|kts|ts|tsx|js|jsx|mjs|cjs|cs|py|go|rb|php|swift|rs|cpp|cc|cxx|c|h|hpp|scala|lua|dart|vue|svelte|astro)$/i;
|
||||
const FILE_EXT = /\.(?:java|kt|kts|ts|tsx|js|jsx|mjs|cjs|cs|py|go|rb|php|swift|rs|cpp|cc|cxx|c|h|hpp|scala|lua|dart|vue|svelte|astro|erl|hrl)$/i;
|
||||
const CALLABLE = new Set(['method', 'function', 'component', 'constructor']);
|
||||
const isTestPath = (p: string) => /(^|\/)(tests?|specs?|__tests__|testdata|mocks?|fixtures?)\//i.test(p) || /\.(test|spec)\.[a-z]+$/i.test(p);
|
||||
const bodyLines = (n: Node) => Math.max(0, (n.endLine ?? n.startLine) - n.startLine);
|
||||
|
||||
Reference in New Issue
Block a user