@optave/codegraph 3.11.0 → 3.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -31
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +91 -60
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts +3 -0
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +83 -49
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.js +78 -62
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.js +61 -42
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/embed.d.ts.map +1 -1
- package/dist/cli/commands/embed.js +49 -4
- package/dist/cli/commands/embed.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +106 -80
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +77 -52
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +132 -121
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/graph/builder/call-resolver.d.ts +71 -0
- package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -0
- package/dist/domain/graph/builder/call-resolver.js +130 -0
- package/dist/domain/graph/builder/call-resolver.js.map +1 -0
- package/dist/domain/graph/builder/helpers.d.ts +4 -4
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +47 -33
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts +6 -0
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +214 -127
- package/dist/domain/graph/builder/incremental.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts +1 -44
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +10 -766
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +151 -192
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.js +82 -65
- package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +84 -56
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +60 -51
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts +8 -6
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.js +107 -122
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
- package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts +14 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.js +77 -0
- package/dist/domain/graph/builder/stages/native-db-lifecycle.js.map +1 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts +62 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.js +747 -0
- package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -0
- package/dist/domain/graph/cycles.d.ts +6 -4
- package/dist/domain/graph/cycles.d.ts.map +1 -1
- package/dist/domain/graph/cycles.js +50 -55
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/journal.d.ts.map +1 -1
- package/dist/domain/graph/journal.js +89 -70
- package/dist/domain/graph/journal.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +10 -4
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +12 -23
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +126 -79
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.d.ts +3 -1
- package/dist/domain/search/generator.d.ts.map +1 -1
- package/dist/domain/search/generator.js +68 -45
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/search/models.d.ts +2 -0
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +37 -3
- package/dist/domain/search/models.js.map +1 -1
- package/dist/domain/search/search/hybrid.d.ts.map +1 -1
- package/dist/domain/search/search/hybrid.js +49 -40
- package/dist/domain/search/search/hybrid.js.map +1 -1
- package/dist/domain/search/search/semantic.d.ts.map +1 -1
- package/dist/domain/search/search/semantic.js +69 -49
- package/dist/domain/search/search/semantic.js.map +1 -1
- package/dist/domain/wasm-worker-entry.js +201 -136
- package/dist/domain/wasm-worker-entry.js.map +1 -1
- package/dist/extractors/elixir.js +95 -71
- package/dist/extractors/elixir.js.map +1 -1
- package/dist/extractors/gleam.d.ts.map +1 -1
- package/dist/extractors/gleam.js +23 -31
- package/dist/extractors/gleam.js.map +1 -1
- package/dist/extractors/helpers.d.ts +79 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +137 -0
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.d.ts.map +1 -1
- package/dist/extractors/java.js +37 -49
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +44 -44
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/julia.js +27 -34
- package/dist/extractors/julia.js.map +1 -1
- package/dist/extractors/r.d.ts.map +1 -1
- package/dist/extractors/r.js +33 -58
- package/dist/extractors/r.js.map +1 -1
- package/dist/extractors/solidity.d.ts.map +1 -1
- package/dist/extractors/solidity.js +38 -61
- package/dist/extractors/solidity.js.map +1 -1
- package/dist/features/boundaries.d.ts.map +1 -1
- package/dist/features/boundaries.js +49 -39
- package/dist/features/boundaries.js.map +1 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +90 -63
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/check.d.ts.map +1 -1
- package/dist/features/check.js +43 -34
- package/dist/features/check.js.map +1 -1
- package/dist/features/cochange.d.ts.map +1 -1
- package/dist/features/cochange.js +68 -56
- package/dist/features/cochange.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +105 -75
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +37 -29
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +31 -22
- package/dist/features/flow.js.map +1 -1
- package/dist/features/graph-enrichment.d.ts.map +1 -1
- package/dist/features/graph-enrichment.js +77 -70
- package/dist/features/graph-enrichment.js.map +1 -1
- package/dist/features/owners.d.ts +17 -26
- package/dist/features/owners.d.ts.map +1 -1
- package/dist/features/owners.js +120 -109
- package/dist/features/owners.js.map +1 -1
- package/dist/features/sequence.d.ts.map +1 -1
- package/dist/features/sequence.js +59 -54
- package/dist/features/sequence.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +60 -60
- package/dist/features/structure-query.js.map +1 -1
- package/dist/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +149 -52
- package/dist/features/structure.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +100 -69
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
- package/dist/graph/classifiers/roles.d.ts.map +1 -1
- package/dist/graph/classifiers/roles.js +63 -59
- package/dist/graph/classifiers/roles.js.map +1 -1
- package/dist/infrastructure/config.d.ts +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +1 -1
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/presentation/cfg.d.ts.map +1 -1
- package/dist/presentation/cfg.js +44 -29
- package/dist/presentation/cfg.js.map +1 -1
- package/dist/presentation/flow.d.ts.map +1 -1
- package/dist/presentation/flow.js +58 -38
- package/dist/presentation/flow.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/package.json +9 -9
- package/src/ast-analysis/engine.ts +145 -61
- package/src/ast-analysis/visitor-utils.ts +86 -46
- package/src/ast-analysis/visitors/ast-store-visitor.ts +104 -69
- package/src/ast-analysis/visitors/dataflow-visitor.ts +86 -47
- package/src/cli/commands/embed.ts +54 -4
- package/src/domain/analysis/dependencies.ts +166 -85
- package/src/domain/analysis/fn-impact.ts +120 -50
- package/src/domain/analysis/module-map.ts +175 -140
- package/src/domain/graph/builder/call-resolver.ts +181 -0
- package/src/domain/graph/builder/helpers.ts +85 -76
- package/src/domain/graph/builder/incremental.ts +321 -152
- package/src/domain/graph/builder/pipeline.ts +19 -957
- package/src/domain/graph/builder/stages/build-edges.ts +229 -275
- package/src/domain/graph/builder/stages/build-structure.ts +115 -82
- package/src/domain/graph/builder/stages/detect-changes.ts +107 -64
- package/src/domain/graph/builder/stages/finalize.ts +72 -70
- package/src/domain/graph/builder/stages/insert-nodes.ts +154 -120
- package/src/domain/graph/builder/stages/native-db-lifecycle.ts +74 -0
- package/src/domain/graph/builder/stages/native-orchestrator.ts +942 -0
- package/src/domain/graph/cycles.ts +51 -49
- package/src/domain/graph/journal.ts +84 -69
- package/src/domain/graph/watcher.ts +12 -4
- package/src/domain/parser.ts +143 -66
- package/src/domain/search/generator.ts +132 -74
- package/src/domain/search/models.ts +39 -3
- package/src/domain/search/search/hybrid.ts +53 -42
- package/src/domain/search/search/semantic.ts +105 -65
- package/src/domain/wasm-worker-entry.ts +235 -152
- package/src/extractors/elixir.ts +91 -64
- package/src/extractors/gleam.ts +33 -37
- package/src/extractors/helpers.ts +205 -1
- package/src/extractors/java.ts +42 -45
- package/src/extractors/javascript.ts +44 -43
- package/src/extractors/julia.ts +28 -35
- package/src/extractors/r.ts +38 -56
- package/src/extractors/solidity.ts +43 -71
- package/src/features/boundaries.ts +64 -46
- package/src/features/cfg.ts +145 -74
- package/src/features/check.ts +60 -43
- package/src/features/cochange.ts +95 -72
- package/src/features/complexity.ts +134 -79
- package/src/features/dataflow.ts +57 -34
- package/src/features/flow.ts +48 -24
- package/src/features/graph-enrichment.ts +105 -70
- package/src/features/owners.ts +186 -146
- package/src/features/sequence.ts +99 -69
- package/src/features/structure-query.ts +94 -79
- package/src/features/structure.ts +199 -79
- package/src/graph/algorithms/leiden/optimiser.ts +142 -87
- package/src/graph/classifiers/roles.ts +64 -54
- package/src/infrastructure/config.ts +1 -1
- package/src/presentation/cfg.ts +48 -32
- package/src/presentation/flow.ts +100 -52
- package/src/types.ts +1 -1
package/src/extractors/gleam.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import type { ExtractorOutput, SubDeclaration, TreeSitterNode, TreeSitterTree } from '../types.js';
|
|
2
|
+
import {
|
|
3
|
+
findChild,
|
|
4
|
+
findFirstChildOfTypes,
|
|
5
|
+
nodeEndLine,
|
|
6
|
+
nodeStartLine,
|
|
7
|
+
pushCall,
|
|
8
|
+
pushImport,
|
|
9
|
+
stripQuotes,
|
|
10
|
+
} from './helpers.js';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Extract symbols from Gleam files.
|
|
@@ -74,7 +76,7 @@ function handleFunction(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
74
76
|
ctx.definitions.push({
|
|
75
77
|
name: nameNode.text,
|
|
76
78
|
kind: 'function',
|
|
77
|
-
line: node
|
|
79
|
+
line: nodeStartLine(node),
|
|
78
80
|
endLine: nodeEndLine(node),
|
|
79
81
|
visibility,
|
|
80
82
|
children: params.length > 0 ? params : undefined,
|
|
@@ -90,7 +92,7 @@ function handleExternalFunction(node: TreeSitterNode, ctx: ExtractorOutput): voi
|
|
|
90
92
|
ctx.definitions.push({
|
|
91
93
|
name: nameNode.text,
|
|
92
94
|
kind: 'function',
|
|
93
|
-
line: node
|
|
95
|
+
line: nodeStartLine(node),
|
|
94
96
|
endLine: nodeEndLine(node),
|
|
95
97
|
visibility: isPublic(node) ? 'public' : 'private',
|
|
96
98
|
children: params.length > 0 ? params : undefined,
|
|
@@ -107,10 +109,7 @@ function handleTypeDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
107
109
|
const child = node.child(i);
|
|
108
110
|
if (!child) continue;
|
|
109
111
|
if (child.type === 'data_constructor' || child.type === 'type_constructor') {
|
|
110
|
-
|
|
111
|
-
if (ctorName) {
|
|
112
|
-
children.push({ name: ctorName.text, kind: 'property', line: child.startPosition.row + 1 });
|
|
113
|
-
}
|
|
112
|
+
pushConstructor(child, children);
|
|
114
113
|
}
|
|
115
114
|
// Recurse into constructors block
|
|
116
115
|
if (child.type === 'data_constructors' || child.type === 'type_constructors') {
|
|
@@ -118,14 +117,7 @@ function handleTypeDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
118
117
|
const ctor = child.child(j);
|
|
119
118
|
if (!ctor) continue;
|
|
120
119
|
if (ctor.type === 'data_constructor' || ctor.type === 'type_constructor') {
|
|
121
|
-
|
|
122
|
-
if (ctorName) {
|
|
123
|
-
children.push({
|
|
124
|
-
name: ctorName.text,
|
|
125
|
-
kind: 'property',
|
|
126
|
-
line: ctor.startPosition.row + 1,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
120
|
+
pushConstructor(ctor, children);
|
|
129
121
|
}
|
|
130
122
|
}
|
|
131
123
|
}
|
|
@@ -134,13 +126,20 @@ function handleTypeDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
134
126
|
ctx.definitions.push({
|
|
135
127
|
name: nameNode.text,
|
|
136
128
|
kind: 'type',
|
|
137
|
-
line: node
|
|
129
|
+
line: nodeStartLine(node),
|
|
138
130
|
endLine: nodeEndLine(node),
|
|
139
131
|
visibility: isPublic(node) ? 'public' : 'private',
|
|
140
132
|
children: children.length > 0 ? children : undefined,
|
|
141
133
|
});
|
|
142
134
|
}
|
|
143
135
|
|
|
136
|
+
function pushConstructor(ctorNode: TreeSitterNode, out: SubDeclaration[]): void {
|
|
137
|
+
const ctorName = ctorNode.childForFieldName('name') || findChild(ctorNode, 'constructor_name');
|
|
138
|
+
if (ctorName) {
|
|
139
|
+
out.push({ name: ctorName.text, kind: 'property', line: nodeStartLine(ctorNode) });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
144
143
|
function handleTypeAlias(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
145
144
|
const nameNode = node.childForFieldName('name') || findChild(node, 'type_name');
|
|
146
145
|
if (!nameNode) return;
|
|
@@ -148,7 +147,7 @@ function handleTypeAlias(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
148
147
|
ctx.definitions.push({
|
|
149
148
|
name: nameNode.text,
|
|
150
149
|
kind: 'type',
|
|
151
|
-
line: node
|
|
150
|
+
line: nodeStartLine(node),
|
|
152
151
|
endLine: nodeEndLine(node),
|
|
153
152
|
visibility: isPublic(node) ? 'public' : 'private',
|
|
154
153
|
});
|
|
@@ -161,7 +160,7 @@ function handleConstant(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
161
160
|
ctx.definitions.push({
|
|
162
161
|
name: nameNode.text,
|
|
163
162
|
kind: 'variable',
|
|
164
|
-
line: node
|
|
163
|
+
line: nodeStartLine(node),
|
|
165
164
|
endLine: nodeEndLine(node),
|
|
166
165
|
visibility: isPublic(node) ? 'public' : 'private',
|
|
167
166
|
});
|
|
@@ -169,7 +168,7 @@ function handleConstant(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
169
168
|
|
|
170
169
|
function handleImport(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
171
170
|
const moduleNode =
|
|
172
|
-
node.childForFieldName('module') ||
|
|
171
|
+
node.childForFieldName('module') || findFirstChildOfTypes(node, ['module', 'string']);
|
|
173
172
|
if (!moduleNode) return;
|
|
174
173
|
|
|
175
174
|
const source = stripQuotes(moduleNode.text);
|
|
@@ -193,11 +192,9 @@ function handleImport(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
193
192
|
names.push(alias.text);
|
|
194
193
|
}
|
|
195
194
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
line: node.startPosition.row + 1,
|
|
200
|
-
});
|
|
195
|
+
// `pushImport` falls back to the source basename when `names` is empty,
|
|
196
|
+
// preserving the previous `source.split('/').pop() || source` default.
|
|
197
|
+
pushImport(ctx, node, source, names);
|
|
201
198
|
}
|
|
202
199
|
|
|
203
200
|
function handleCall(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
@@ -205,16 +202,15 @@ function handleCall(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
205
202
|
if (!funcNode) return;
|
|
206
203
|
|
|
207
204
|
if (funcNode.type === 'identifier' || funcNode.type === 'variable') {
|
|
208
|
-
ctx
|
|
205
|
+
pushCall(ctx, node, funcNode.text);
|
|
209
206
|
} else if (funcNode.type === 'field_access' || funcNode.type === 'module_select') {
|
|
210
207
|
const field = funcNode.childForFieldName('field') || funcNode.childForFieldName('label');
|
|
211
208
|
// Prefer the `record` field; fall back to first named child to skip
|
|
212
209
|
// anonymous punctuation tokens (the `.` between record and field).
|
|
213
210
|
const record = funcNode.childForFieldName('record') || funcNode.namedChild(0);
|
|
214
211
|
if (field) {
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
ctx.calls.push(call);
|
|
212
|
+
const receiver = record && record !== field ? record.text : undefined;
|
|
213
|
+
pushCall(ctx, node, field.text, receiver !== undefined ? { receiver } : {});
|
|
218
214
|
}
|
|
219
215
|
}
|
|
220
216
|
}
|
|
@@ -231,11 +227,11 @@ function extractParams(funcNode: TreeSitterNode): SubDeclaration[] {
|
|
|
231
227
|
if (param.type === 'function_parameter' || param.type === 'parameter') {
|
|
232
228
|
const nameNode = param.childForFieldName('name') || findChild(param, 'identifier');
|
|
233
229
|
if (nameNode) {
|
|
234
|
-
params.push({ name: nameNode.text, kind: 'parameter', line: param
|
|
230
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: nodeStartLine(param) });
|
|
235
231
|
}
|
|
236
232
|
}
|
|
237
233
|
if (param.type === 'identifier') {
|
|
238
|
-
params.push({ name: param.text, kind: 'parameter', line: param
|
|
234
|
+
params.push({ name: param.text, kind: 'parameter', line: nodeStartLine(param) });
|
|
239
235
|
}
|
|
240
236
|
}
|
|
241
237
|
return params;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Call,
|
|
3
|
+
ExtractorOutput,
|
|
4
|
+
Import,
|
|
5
|
+
SubDeclaration,
|
|
6
|
+
TreeSitterNode,
|
|
7
|
+
TypeMapEntry,
|
|
8
|
+
} from '../types.js';
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
11
|
* Maximum recursion depth for tree-sitter AST walkers.
|
|
@@ -6,6 +13,11 @@ import type { SubDeclaration, TreeSitterNode, TypeMapEntry } from '../types.js';
|
|
|
6
13
|
*/
|
|
7
14
|
export const MAX_WALK_DEPTH = 200;
|
|
8
15
|
|
|
16
|
+
/** Convert a tree-sitter node's start row to a 1-based source line. */
|
|
17
|
+
export function nodeStartLine(node: TreeSitterNode): number {
|
|
18
|
+
return node.startPosition.row + 1;
|
|
19
|
+
}
|
|
20
|
+
|
|
9
21
|
export function nodeEndLine(node: TreeSitterNode): number {
|
|
10
22
|
return node.endPosition.row + 1;
|
|
11
23
|
}
|
|
@@ -18,6 +30,56 @@ export function findChild(node: TreeSitterNode, type: string): TreeSitterNode |
|
|
|
18
30
|
return null;
|
|
19
31
|
}
|
|
20
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Find the first child whose type is in `types`. Useful when several grammar
|
|
35
|
+
* variants name the same conceptual node differently (e.g. `string` vs
|
|
36
|
+
* `string_literal`). Returns the first match in document order, or null.
|
|
37
|
+
*/
|
|
38
|
+
export function findFirstChildOfTypes(
|
|
39
|
+
node: TreeSitterNode,
|
|
40
|
+
types: readonly string[],
|
|
41
|
+
): TreeSitterNode | null {
|
|
42
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
43
|
+
const child = node.child(i);
|
|
44
|
+
if (child && types.includes(child.type)) return child;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Iterate the direct children of `node` in document order, skipping nulls and
|
|
51
|
+
* tokens whose type appears in `skipTypes`. Mirrors the common
|
|
52
|
+
* `for (let i = 0; i < node.childCount; i++) { const c = node.child(i); if (...) continue; ... }`
|
|
53
|
+
* idiom while letting callers filter out grammar punctuation (`,`, `(`, `{`, etc.).
|
|
54
|
+
*/
|
|
55
|
+
export function* iterChildren(
|
|
56
|
+
node: TreeSitterNode,
|
|
57
|
+
skipTypes: ReadonlySet<string> = EMPTY_SKIP_SET,
|
|
58
|
+
): Generator<TreeSitterNode> {
|
|
59
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
60
|
+
const child = node.child(i);
|
|
61
|
+
if (!child) continue;
|
|
62
|
+
if (skipTypes.has(child.type)) continue;
|
|
63
|
+
yield child;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const EMPTY_SKIP_SET: ReadonlySet<string> = new Set();
|
|
68
|
+
|
|
69
|
+
/** Common punctuation tokens — handy as a `skipTypes` set for `iterChildren`. */
|
|
70
|
+
export const PUNCTUATION_TOKENS: ReadonlySet<string> = new Set([
|
|
71
|
+
',',
|
|
72
|
+
';',
|
|
73
|
+
'(',
|
|
74
|
+
')',
|
|
75
|
+
'[',
|
|
76
|
+
']',
|
|
77
|
+
'{',
|
|
78
|
+
'}',
|
|
79
|
+
':',
|
|
80
|
+
'.',
|
|
81
|
+
]);
|
|
82
|
+
|
|
21
83
|
/**
|
|
22
84
|
* Merge a type-map entry, keeping the higher-confidence one.
|
|
23
85
|
* Shared across all language extractors that build type maps for call resolution.
|
|
@@ -197,3 +259,145 @@ export function extractModifierVisibility(
|
|
|
197
259
|
}
|
|
198
260
|
return undefined;
|
|
199
261
|
}
|
|
262
|
+
|
|
263
|
+
// ── Output-push helpers ────────────────────────────────────────────────────
|
|
264
|
+
//
|
|
265
|
+
// Most extractors finish with `ctx.calls.push({ name, line: node.startPosition.row + 1 })`
|
|
266
|
+
// or `ctx.imports.push({ source, names, line: node.startPosition.row + 1 })`.
|
|
267
|
+
// Centralising the construction keeps `line` derivation consistent and removes
|
|
268
|
+
// the ~108 hand-rolled `startPosition.row + 1` literals scattered across
|
|
269
|
+
// language extractors.
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Append a `Call` to the extractor output. `line` defaults to the start line of
|
|
273
|
+
* `node`; pass `extra` for `receiver` / `dynamic` flags.
|
|
274
|
+
*/
|
|
275
|
+
export function pushCall(
|
|
276
|
+
ctx: ExtractorOutput,
|
|
277
|
+
node: TreeSitterNode,
|
|
278
|
+
name: string,
|
|
279
|
+
extra: { receiver?: string; dynamic?: boolean } = {},
|
|
280
|
+
): void {
|
|
281
|
+
if (!name) return;
|
|
282
|
+
const call: Call = { name, line: nodeStartLine(node) };
|
|
283
|
+
if (extra.receiver !== undefined) call.receiver = extra.receiver;
|
|
284
|
+
if (extra.dynamic !== undefined) call.dynamic = extra.dynamic;
|
|
285
|
+
ctx.calls.push(call);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Append an `Import` to the extractor output. `line` defaults to the start
|
|
290
|
+
* line of `node`. If `names` is empty, the source basename (split on `/`) is
|
|
291
|
+
* used as a single-name fallback — matching the convention in gleam, julia,
|
|
292
|
+
* and similar module-path imports.
|
|
293
|
+
*/
|
|
294
|
+
export function pushImport(
|
|
295
|
+
ctx: ExtractorOutput,
|
|
296
|
+
node: TreeSitterNode,
|
|
297
|
+
source: string,
|
|
298
|
+
names: string[],
|
|
299
|
+
flags: Partial<Omit<Import, 'source' | 'names' | 'line'>> = {},
|
|
300
|
+
): void {
|
|
301
|
+
if (!source) return;
|
|
302
|
+
const resolved = names.length > 0 ? names : [lastPathSegment(source, '/') || source];
|
|
303
|
+
const entry: Import = { source, names: resolved, line: nodeStartLine(node) };
|
|
304
|
+
Object.assign(entry, flags);
|
|
305
|
+
ctx.imports.push(entry);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ── Parameter extraction ───────────────────────────────────────────────────
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Options for {@link extractSimpleParameters}.
|
|
312
|
+
*/
|
|
313
|
+
export interface ExtractParametersOptions {
|
|
314
|
+
/** Tree-sitter types that mark a single parameter node (e.g. `formal_parameter`). */
|
|
315
|
+
paramTypes: readonly string[];
|
|
316
|
+
/**
|
|
317
|
+
* Field name on each parameter that holds the bound identifier. Defaults to
|
|
318
|
+
* `'name'`. Pass `null` to use the parameter node itself when its type is in
|
|
319
|
+
* `paramTypes` and it has no `name` field (e.g. R's bare `identifier`).
|
|
320
|
+
*/
|
|
321
|
+
nameField?: string | null;
|
|
322
|
+
/**
|
|
323
|
+
* If true, when `nameField` lookup fails fall back to the first `identifier`
|
|
324
|
+
* child of the parameter. Useful for Gleam / Solidity-style grammars.
|
|
325
|
+
*/
|
|
326
|
+
fallbackToIdentifier?: boolean;
|
|
327
|
+
/**
|
|
328
|
+
* Optional type-map sink. When provided, the parameter's `type` field text
|
|
329
|
+
* (if present) is recorded with the given confidence.
|
|
330
|
+
*/
|
|
331
|
+
typeMap?: Map<string, TypeMapEntry>;
|
|
332
|
+
/** Confidence used when writing into `typeMap`. Defaults to `0.9`. */
|
|
333
|
+
typeMapConfidence?: number;
|
|
334
|
+
/**
|
|
335
|
+
* Optional callback to derive the type text from the parameter's `type`
|
|
336
|
+
* field node. Defaults to `node.text`. Use this for languages where the
|
|
337
|
+
* `type` field is wrapped (e.g. Java `generic_type` → first child).
|
|
338
|
+
*/
|
|
339
|
+
resolveType?: (typeNode: TreeSitterNode) => string | undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Extract parameters from a parameter-list node using a uniform pattern.
|
|
344
|
+
*
|
|
345
|
+
* This collapses the boilerplate in `extract*Params` helpers across
|
|
346
|
+
* Java/Julia/Gleam/Solidity/R/etc. — each one walks the parameter list,
|
|
347
|
+
* matches a parameter node type, reads the `name` field, and pushes a
|
|
348
|
+
* `SubDeclaration` with `kind: 'parameter'`.
|
|
349
|
+
*/
|
|
350
|
+
export function extractSimpleParameters(
|
|
351
|
+
paramListNode: TreeSitterNode | null,
|
|
352
|
+
options: ExtractParametersOptions,
|
|
353
|
+
): SubDeclaration[] {
|
|
354
|
+
const params: SubDeclaration[] = [];
|
|
355
|
+
if (!paramListNode) return params;
|
|
356
|
+
const { paramTypes, nameField = 'name', fallbackToIdentifier = false } = options;
|
|
357
|
+
|
|
358
|
+
for (let i = 0; i < paramListNode.childCount; i++) {
|
|
359
|
+
const param = paramListNode.child(i);
|
|
360
|
+
if (!param || !paramTypes.includes(param.type)) continue;
|
|
361
|
+
const nameNode = resolveParamName(param, nameField, fallbackToIdentifier);
|
|
362
|
+
if (!nameNode) continue;
|
|
363
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: nodeStartLine(param) });
|
|
364
|
+
recordParamType(param, nameNode.text, options);
|
|
365
|
+
}
|
|
366
|
+
return params;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** Record a parameter's declared type into the type-map sink, if configured. */
|
|
370
|
+
function recordParamType(
|
|
371
|
+
param: TreeSitterNode,
|
|
372
|
+
paramName: string,
|
|
373
|
+
options: ExtractParametersOptions,
|
|
374
|
+
): void {
|
|
375
|
+
const { typeMap, resolveType, typeMapConfidence = 0.9 } = options;
|
|
376
|
+
if (!typeMap) return;
|
|
377
|
+
const typeNode = param.childForFieldName('type');
|
|
378
|
+
if (!typeNode) return;
|
|
379
|
+
const typeText = resolveType ? resolveType(typeNode) : typeNode.text;
|
|
380
|
+
if (!typeText) return;
|
|
381
|
+
setTypeMapEntry(typeMap, paramName, typeText, typeMapConfidence);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Resolve the identifier node that names a parameter. Used by
|
|
386
|
+
* {@link extractSimpleParameters}; exposed so language-specific extractors
|
|
387
|
+
* can reuse the same lookup logic in custom loops.
|
|
388
|
+
*/
|
|
389
|
+
export function resolveParamName(
|
|
390
|
+
paramNode: TreeSitterNode,
|
|
391
|
+
nameField: string | null,
|
|
392
|
+
fallbackToIdentifier: boolean,
|
|
393
|
+
): TreeSitterNode | null {
|
|
394
|
+
if (nameField === null) {
|
|
395
|
+
return paramNode;
|
|
396
|
+
}
|
|
397
|
+
const named = paramNode.childForFieldName(nameField);
|
|
398
|
+
if (named) return named;
|
|
399
|
+
if (fallbackToIdentifier) {
|
|
400
|
+
return findChild(paramNode, 'identifier');
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
package/src/extractors/java.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
Call,
|
|
3
2
|
ExtractorOutput,
|
|
4
3
|
SubDeclaration,
|
|
5
4
|
TreeSitterNode,
|
|
@@ -9,10 +8,14 @@ import type {
|
|
|
9
8
|
import {
|
|
10
9
|
extractBodyMembers,
|
|
11
10
|
extractModifierVisibility,
|
|
11
|
+
extractSimpleParameters,
|
|
12
12
|
findChild,
|
|
13
13
|
findParentNode,
|
|
14
14
|
lastPathSegment,
|
|
15
15
|
nodeEndLine,
|
|
16
|
+
nodeStartLine,
|
|
17
|
+
pushCall,
|
|
18
|
+
pushImport,
|
|
16
19
|
} from './helpers.js';
|
|
17
20
|
|
|
18
21
|
/**
|
|
@@ -78,7 +81,7 @@ function handleJavaClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
78
81
|
ctx.definitions.push({
|
|
79
82
|
name: nameNode.text,
|
|
80
83
|
kind: 'class',
|
|
81
|
-
line: node
|
|
84
|
+
line: nodeStartLine(node),
|
|
82
85
|
endLine: nodeEndLine(node),
|
|
83
86
|
children: classChildren.length > 0 ? classChildren : undefined,
|
|
84
87
|
});
|
|
@@ -87,7 +90,7 @@ function handleJavaClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
87
90
|
|
|
88
91
|
const interfaces = node.childForFieldName('interfaces');
|
|
89
92
|
if (interfaces) {
|
|
90
|
-
extractJavaInterfaces(interfaces, nameNode.text, node
|
|
93
|
+
extractJavaInterfaces(interfaces, nameNode.text, nodeStartLine(node), ctx);
|
|
91
94
|
}
|
|
92
95
|
}
|
|
93
96
|
|
|
@@ -101,7 +104,7 @@ function extractJavaSuperclass(
|
|
|
101
104
|
if (!superclass) return;
|
|
102
105
|
const superName = findJavaSuperTypeName(superclass);
|
|
103
106
|
if (superName) {
|
|
104
|
-
ctx.classes.push({ name: className, extends: superName, line: node
|
|
107
|
+
ctx.classes.push({ name: className, extends: superName, line: nodeStartLine(node) });
|
|
105
108
|
}
|
|
106
109
|
}
|
|
107
110
|
|
|
@@ -163,7 +166,7 @@ function handleJavaInterfaceDecl(node: TreeSitterNode, ctx: ExtractorOutput): vo
|
|
|
163
166
|
ctx.definitions.push({
|
|
164
167
|
name: nameNode.text,
|
|
165
168
|
kind: 'interface',
|
|
166
|
-
line: node
|
|
169
|
+
line: nodeStartLine(node),
|
|
167
170
|
endLine: nodeEndLine(node),
|
|
168
171
|
});
|
|
169
172
|
const body = node.childForFieldName('body');
|
|
@@ -184,8 +187,8 @@ function extractJavaInterfaceMethods(
|
|
|
184
187
|
ctx.definitions.push({
|
|
185
188
|
name: `${ifaceName}.${methName.text}`,
|
|
186
189
|
kind: 'method',
|
|
187
|
-
line: child
|
|
188
|
-
endLine: child
|
|
190
|
+
line: nodeStartLine(child),
|
|
191
|
+
endLine: nodeEndLine(child),
|
|
189
192
|
});
|
|
190
193
|
}
|
|
191
194
|
}
|
|
@@ -199,7 +202,7 @@ function handleJavaEnumDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
199
202
|
ctx.definitions.push({
|
|
200
203
|
name: nameNode.text,
|
|
201
204
|
kind: 'enum',
|
|
202
|
-
line: node
|
|
205
|
+
line: nodeStartLine(node),
|
|
203
206
|
endLine: nodeEndLine(node),
|
|
204
207
|
children: enumChildren.length > 0 ? enumChildren : undefined,
|
|
205
208
|
});
|
|
@@ -216,7 +219,7 @@ function handleJavaMethodDecl(node: TreeSitterNode, ctx: ExtractorOutput): void
|
|
|
216
219
|
ctx.definitions.push({
|
|
217
220
|
name: fullName,
|
|
218
221
|
kind: 'method',
|
|
219
|
-
line: node
|
|
222
|
+
line: nodeStartLine(node),
|
|
220
223
|
endLine: nodeEndLine(node),
|
|
221
224
|
children: params.length > 0 ? params : undefined,
|
|
222
225
|
visibility: extractModifierVisibility(node),
|
|
@@ -232,7 +235,7 @@ function handleJavaConstructorDecl(node: TreeSitterNode, ctx: ExtractorOutput):
|
|
|
232
235
|
ctx.definitions.push({
|
|
233
236
|
name: fullName,
|
|
234
237
|
kind: 'method',
|
|
235
|
-
line: node
|
|
238
|
+
line: nodeStartLine(node),
|
|
236
239
|
endLine: nodeEndLine(node),
|
|
237
240
|
children: params.length > 0 ? params : undefined,
|
|
238
241
|
visibility: extractModifierVisibility(node),
|
|
@@ -245,12 +248,7 @@ function handleJavaImportDecl(node: TreeSitterNode, ctx: ExtractorOutput): void
|
|
|
245
248
|
if (child && (child.type === 'scoped_identifier' || child.type === 'identifier')) {
|
|
246
249
|
const fullPath = child.text;
|
|
247
250
|
const lastName = lastPathSegment(fullPath, '.');
|
|
248
|
-
ctx
|
|
249
|
-
source: fullPath,
|
|
250
|
-
names: [lastName],
|
|
251
|
-
line: node.startPosition.row + 1,
|
|
252
|
-
javaImport: true,
|
|
253
|
-
});
|
|
251
|
+
pushImport(ctx, node, fullPath, [lastName], { javaImport: true });
|
|
254
252
|
}
|
|
255
253
|
if (child && child.type === 'asterisk') {
|
|
256
254
|
const lastImport = ctx.imports[ctx.imports.length - 1];
|
|
@@ -263,21 +261,25 @@ function handleJavaMethodInvocation(node: TreeSitterNode, ctx: ExtractorOutput):
|
|
|
263
261
|
const nameNode = node.childForFieldName('name');
|
|
264
262
|
if (!nameNode) return;
|
|
265
263
|
const obj = node.childForFieldName('object');
|
|
266
|
-
|
|
267
|
-
if (obj) call.receiver = obj.text;
|
|
268
|
-
ctx.calls.push(call);
|
|
264
|
+
pushCall(ctx, node, nameNode.text, obj ? { receiver: obj.text } : {});
|
|
269
265
|
}
|
|
270
266
|
|
|
271
267
|
function handleJavaLocalVarDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
272
268
|
const typeNode = node.childForFieldName('type');
|
|
273
269
|
if (!typeNode) return;
|
|
274
|
-
const typeName = typeNode
|
|
270
|
+
const typeName = resolveJavaTypeText(typeNode);
|
|
275
271
|
if (!typeName) return;
|
|
276
272
|
for (let i = 0; i < node.childCount; i++) {
|
|
277
273
|
const child = node.child(i);
|
|
278
274
|
if (child?.type === 'variable_declarator') {
|
|
279
275
|
const nameNode = child.childForFieldName('name');
|
|
280
|
-
|
|
276
|
+
// Use direct Map.set (last-wins) for local variable declarations.
|
|
277
|
+
// Local variable types are method-scoped and should override any
|
|
278
|
+
// prior entry (e.g. a same-named constructor parameter). Using
|
|
279
|
+
// setTypeMapEntry (first-wins on tie) would let a constructor
|
|
280
|
+
// parameter type block a local variable's more-specific concrete type.
|
|
281
|
+
if (nameNode && ctx.typeMap)
|
|
282
|
+
ctx.typeMap.set(nameNode.text, { type: typeName, confidence: 0.9 });
|
|
281
283
|
}
|
|
282
284
|
}
|
|
283
285
|
}
|
|
@@ -285,8 +287,17 @@ function handleJavaLocalVarDecl(node: TreeSitterNode, ctx: ExtractorOutput): voi
|
|
|
285
287
|
function handleJavaObjectCreation(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
286
288
|
const typeNode = node.childForFieldName('type');
|
|
287
289
|
if (!typeNode) return;
|
|
288
|
-
const typeName = typeNode
|
|
289
|
-
if (typeName) ctx
|
|
290
|
+
const typeName = resolveJavaTypeText(typeNode);
|
|
291
|
+
if (typeName) pushCall(ctx, node, typeName);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Resolve a Java type node's text, unwrapping `generic_type` to its base name.
|
|
296
|
+
* Used wherever we need the bare type identifier (local var decls, object
|
|
297
|
+
* creation, parameter types).
|
|
298
|
+
*/
|
|
299
|
+
function resolveJavaTypeText(typeNode: TreeSitterNode): string | undefined {
|
|
300
|
+
return typeNode.type === 'generic_type' ? typeNode.child(0)?.text : typeNode.text;
|
|
290
301
|
}
|
|
291
302
|
|
|
292
303
|
const JAVA_PARENT_TYPES = [
|
|
@@ -300,31 +311,17 @@ function findJavaParentClass(node: TreeSitterNode): string | null {
|
|
|
300
311
|
|
|
301
312
|
// ── Child extraction helpers ────────────────────────────────────────────────
|
|
302
313
|
|
|
314
|
+
const JAVA_PARAM_TYPES = ['formal_parameter', 'spread_parameter'] as const;
|
|
315
|
+
|
|
303
316
|
function extractJavaParameters(
|
|
304
317
|
paramListNode: TreeSitterNode | null,
|
|
305
318
|
typeMap?: Map<string, TypeMapEntry>,
|
|
306
319
|
): SubDeclaration[] {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (param.type === 'formal_parameter' || param.type === 'spread_parameter') {
|
|
313
|
-
const nameNode = param.childForFieldName('name');
|
|
314
|
-
if (nameNode) {
|
|
315
|
-
params.push({ name: nameNode.text, kind: 'parameter', line: param.startPosition.row + 1 });
|
|
316
|
-
if (typeMap) {
|
|
317
|
-
const typeNode = param.childForFieldName('type');
|
|
318
|
-
if (typeNode) {
|
|
319
|
-
const typeName =
|
|
320
|
-
typeNode.type === 'generic_type' ? typeNode.child(0)?.text : typeNode.text;
|
|
321
|
-
if (typeName) typeMap.set(nameNode.text, { type: typeName, confidence: 0.9 });
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
return params;
|
|
320
|
+
return extractSimpleParameters(paramListNode, {
|
|
321
|
+
paramTypes: JAVA_PARAM_TYPES,
|
|
322
|
+
typeMap,
|
|
323
|
+
resolveType: resolveJavaTypeText,
|
|
324
|
+
});
|
|
328
325
|
}
|
|
329
326
|
|
|
330
327
|
function extractClassFields(classNode: TreeSitterNode): SubDeclaration[] {
|
|
@@ -350,7 +347,7 @@ function extractFieldDeclarators(member: TreeSitterNode, fields: SubDeclaration[
|
|
|
350
347
|
fields.push({
|
|
351
348
|
name: nameNode.text,
|
|
352
349
|
kind: 'property',
|
|
353
|
-
line: member
|
|
350
|
+
line: nodeStartLine(member),
|
|
354
351
|
visibility: vis,
|
|
355
352
|
});
|
|
356
353
|
}
|