@optave/codegraph 3.8.0 → 3.8.1
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 +9 -8
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +95 -87
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/metrics.d.ts +0 -3
- package/dist/ast-analysis/metrics.d.ts.map +1 -1
- package/dist/ast-analysis/metrics.js +30 -13
- package/dist/ast-analysis/metrics.js.map +1 -1
- package/dist/ast-analysis/shared.d.ts.map +1 -1
- package/dist/ast-analysis/shared.js +24 -19
- package/dist/ast-analysis/shared.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +55 -39
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitor.js +91 -70
- package/dist/ast-analysis/visitor.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 +54 -58
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.js +32 -39
- package/dist/ast-analysis/visitors/complexity-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 +57 -38
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +16 -2
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +29 -26
- package/dist/db/connection.js.map +1 -1
- package/dist/db/query-builder.d.ts.map +1 -1
- package/dist/db/query-builder.js +16 -5
- package/dist/db/query-builder.js.map +1 -1
- package/dist/db/repository/base.d.ts +10 -0
- package/dist/db/repository/base.d.ts.map +1 -1
- package/dist/db/repository/base.js +17 -0
- package/dist/db/repository/base.js.map +1 -1
- package/dist/db/repository/native-repository.d.ts +6 -1
- package/dist/db/repository/native-repository.d.ts.map +1 -1
- package/dist/db/repository/native-repository.js +77 -1
- package/dist/db/repository/native-repository.js.map +1 -1
- package/dist/db/repository/nodes.d.ts.map +1 -1
- package/dist/db/repository/nodes.js +8 -4
- package/dist/db/repository/nodes.js.map +1 -1
- package/dist/db/repository/sqlite-repository.d.ts +3 -0
- package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
- package/dist/db/repository/sqlite-repository.js +26 -0
- package/dist/db/repository/sqlite-repository.js.map +1 -1
- package/dist/domain/analysis/brief.d.ts.map +1 -1
- package/dist/domain/analysis/brief.js +13 -17
- package/dist/domain/analysis/brief.js.map +1 -1
- package/dist/domain/analysis/context.d.ts.map +1 -1
- package/dist/domain/analysis/context.js +14 -11
- package/dist/domain/analysis/context.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +53 -52
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts +2 -7
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +33 -31
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/implementations.d.ts.map +1 -1
- package/dist/domain/analysis/implementations.js +11 -19
- package/dist/domain/analysis/implementations.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +55 -76
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/query-helpers.d.ts +7 -0
- package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
- package/dist/domain/analysis/query-helpers.js +15 -1
- package/dist/domain/analysis/query-helpers.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +255 -105
- 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 +22 -17
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +2 -2
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +2 -2
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.js +32 -21
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +95 -84
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/cycles.d.ts +6 -0
- package/dist/domain/graph/cycles.d.ts.map +1 -1
- package/dist/domain/graph/cycles.js +114 -22
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/resolve.js +1 -1
- package/dist/domain/graph/resolve.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts +2 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +170 -75
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +0 -5
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +13 -28
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.js +1 -1
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/search/models.d.ts +4 -3
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +18 -5
- 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 +29 -18
- package/dist/domain/search/search/hybrid.js.map +1 -1
- package/dist/extractors/go.js +36 -33
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +40 -29
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.js +58 -46
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.js +46 -45
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.js +84 -78
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/python.js +29 -24
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/rust.js +41 -32
- package/dist/extractors/rust.js.map +1 -1
- package/dist/extractors/solidity.js +58 -67
- package/dist/extractors/solidity.js.map +1 -1
- package/dist/extractors/swift.js +83 -81
- package/dist/extractors/swift.js.map +1 -1
- package/dist/extractors/zig.js +58 -60
- package/dist/extractors/zig.js.map +1 -1
- package/dist/features/ast.d.ts +16 -14
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +83 -81
- package/dist/features/ast.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +8 -6
- package/dist/features/audit.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +69 -72
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/communities.d.ts.map +1 -1
- package/dist/features/communities.js +19 -7
- package/dist/features/communities.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +120 -125
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +136 -137
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +84 -79
- package/dist/features/flow.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +69 -65
- package/dist/features/structure-query.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +70 -55
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
- package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/partition.js +288 -266
- package/dist/graph/algorithms/leiden/partition.js.map +1 -1
- package/dist/graph/model.d.ts.map +1 -1
- package/dist/graph/model.js +5 -1
- package/dist/graph/model.js.map +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +6 -4
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/suppress.d.ts +25 -0
- package/dist/infrastructure/suppress.d.ts.map +1 -0
- package/dist/infrastructure/suppress.js +43 -0
- package/dist/infrastructure/suppress.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +29 -24
- package/dist/mcp/server.js.map +1 -1
- package/dist/presentation/dataflow.d.ts.map +1 -1
- package/dist/presentation/dataflow.js +47 -38
- package/dist/presentation/dataflow.js.map +1 -1
- package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
- package/dist/presentation/diff-impact-mermaid.js +60 -51
- package/dist/presentation/diff-impact-mermaid.js.map +1 -1
- package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
- package/dist/presentation/queries-cli/exports.js +20 -14
- package/dist/presentation/queries-cli/exports.js.map +1 -1
- package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
- package/dist/presentation/queries-cli/impact.js +15 -13
- package/dist/presentation/queries-cli/impact.js.map +1 -1
- package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
- package/dist/presentation/queries-cli/inspect.js +101 -79
- package/dist/presentation/queries-cli/inspect.js.map +1 -1
- package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
- package/dist/presentation/queries-cli/overview.js +25 -16
- package/dist/presentation/queries-cli/overview.js.map +1 -1
- package/dist/presentation/queries-cli/path.js +26 -20
- package/dist/presentation/queries-cli/path.js.map +1 -1
- package/dist/presentation/result-formatter.d.ts +10 -0
- package/dist/presentation/result-formatter.d.ts.map +1 -1
- package/dist/presentation/result-formatter.js +16 -1
- package/dist/presentation/result-formatter.js.map +1 -1
- package/dist/presentation/viewer.d.ts.map +1 -1
- package/dist/presentation/viewer.js +18 -12
- package/dist/presentation/viewer.js.map +1 -1
- package/dist/shared/errors.d.ts +5 -0
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +5 -0
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/hierarchy.d.ts +8 -2
- package/dist/shared/hierarchy.d.ts.map +1 -1
- package/dist/shared/hierarchy.js +42 -1
- package/dist/shared/hierarchy.js.map +1 -1
- package/dist/shared/normalize.d.ts +6 -1
- package/dist/shared/normalize.d.ts.map +1 -1
- package/dist/shared/normalize.js +20 -12
- package/dist/shared/normalize.js.map +1 -1
- package/dist/shared/paginate.d.ts +0 -9
- package/dist/shared/paginate.d.ts.map +1 -1
- package/dist/shared/paginate.js +0 -15
- package/dist/shared/paginate.js.map +1 -1
- package/dist/types.d.ts +10 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/ast-analysis/engine.ts +126 -105
- package/src/ast-analysis/metrics.ts +33 -11
- package/src/ast-analysis/shared.ts +33 -24
- package/src/ast-analysis/visitor-utils.ts +52 -32
- package/src/ast-analysis/visitor.ts +132 -71
- package/src/ast-analysis/visitors/ast-store-visitor.ts +53 -50
- package/src/ast-analysis/visitors/complexity-visitor.ts +35 -40
- package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
- package/src/cli/commands/watch.ts +16 -2
- package/src/db/connection.ts +29 -28
- package/src/db/query-builder.ts +15 -3
- package/src/db/repository/base.ts +20 -0
- package/src/db/repository/native-repository.ts +79 -1
- package/src/db/repository/nodes.ts +13 -8
- package/src/db/repository/sqlite-repository.ts +29 -0
- package/src/domain/analysis/brief.ts +15 -25
- package/src/domain/analysis/context.ts +17 -10
- package/src/domain/analysis/dependencies.ts +67 -76
- package/src/domain/analysis/fn-impact.ts +36 -43
- package/src/domain/analysis/implementations.ts +11 -17
- package/src/domain/analysis/module-map.ts +58 -92
- package/src/domain/analysis/query-helpers.ts +18 -1
- package/src/domain/graph/builder/pipeline.ts +286 -97
- package/src/domain/graph/builder/stages/build-edges.ts +22 -18
- package/src/domain/graph/builder/stages/detect-changes.ts +2 -2
- package/src/domain/graph/builder/stages/finalize.ts +2 -2
- package/src/domain/graph/builder/stages/insert-nodes.ts +59 -34
- package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
- package/src/domain/graph/cycles.ts +110 -23
- package/src/domain/graph/resolve.ts +1 -1
- package/src/domain/graph/watcher.ts +202 -96
- package/src/domain/parser.ts +14 -26
- package/src/domain/search/generator.ts +1 -1
- package/src/domain/search/models.ts +17 -4
- package/src/domain/search/search/hybrid.ts +69 -51
- package/src/extractors/go.ts +43 -33
- package/src/extractors/helpers.ts +37 -23
- package/src/extractors/java.ts +66 -47
- package/src/extractors/javascript.ts +45 -46
- package/src/extractors/kotlin.ts +84 -77
- package/src/extractors/python.ts +31 -25
- package/src/extractors/rust.ts +37 -29
- package/src/extractors/solidity.ts +57 -61
- package/src/extractors/swift.ts +81 -80
- package/src/extractors/zig.ts +58 -61
- package/src/features/ast.ts +130 -110
- package/src/features/audit.ts +8 -6
- package/src/features/branch-compare.ts +105 -79
- package/src/features/communities.ts +25 -10
- package/src/features/complexity.ts +171 -134
- package/src/features/dataflow.ts +165 -175
- package/src/features/flow.ts +129 -92
- package/src/features/structure-query.ts +79 -64
- package/src/graph/algorithms/leiden/optimiser.ts +99 -55
- package/src/graph/algorithms/leiden/partition.ts +359 -294
- package/src/graph/model.ts +6 -1
- package/src/infrastructure/config.ts +6 -4
- package/src/infrastructure/suppress.ts +47 -0
- package/src/mcp/server.ts +53 -37
- package/src/presentation/dataflow.ts +50 -44
- package/src/presentation/diff-impact-mermaid.ts +104 -62
- package/src/presentation/queries-cli/exports.ts +21 -13
- package/src/presentation/queries-cli/impact.ts +15 -13
- package/src/presentation/queries-cli/inspect.ts +100 -81
- package/src/presentation/queries-cli/overview.ts +26 -16
- package/src/presentation/queries-cli/path.ts +33 -25
- package/src/presentation/result-formatter.ts +19 -1
- package/src/presentation/viewer.ts +42 -14
- package/src/shared/errors.ts +6 -0
- package/src/shared/hierarchy.ts +50 -2
- package/src/shared/normalize.ts +31 -12
- package/src/shared/paginate.ts +0 -17
- package/src/types.ts +24 -4
package/src/extractors/go.ts
CHANGED
|
@@ -80,21 +80,9 @@ function handleGoFuncDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
80
80
|
|
|
81
81
|
function handleGoMethodDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
82
82
|
const nameNode = node.childForFieldName('name');
|
|
83
|
-
const receiver = node.childForFieldName('receiver');
|
|
84
83
|
if (!nameNode) return;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
for (let i = 0; i < receiver.childCount; i++) {
|
|
88
|
-
const param = receiver.child(i);
|
|
89
|
-
if (!param) continue;
|
|
90
|
-
const typeNode = param.childForFieldName('type');
|
|
91
|
-
if (typeNode) {
|
|
92
|
-
receiverType =
|
|
93
|
-
typeNode.type === 'pointer_type' ? typeNode.text.replace(/^\*/, '') : typeNode.text;
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
84
|
+
const receiver = node.childForFieldName('receiver');
|
|
85
|
+
const receiverType = receiver ? extractGoReceiverType(receiver) : null;
|
|
98
86
|
const fullName = receiverType ? `${receiverType}.${nameNode.text}` : nameNode.text;
|
|
99
87
|
const params = extractGoParameters(node.childForFieldName('parameters'));
|
|
100
88
|
ctx.definitions.push({
|
|
@@ -107,6 +95,19 @@ function handleGoMethodDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
107
95
|
});
|
|
108
96
|
}
|
|
109
97
|
|
|
98
|
+
/** Extract the receiver type name from a method receiver parameter list. */
|
|
99
|
+
function extractGoReceiverType(receiver: TreeSitterNode): string | null {
|
|
100
|
+
for (let i = 0; i < receiver.childCount; i++) {
|
|
101
|
+
const param = receiver.child(i);
|
|
102
|
+
if (!param) continue;
|
|
103
|
+
const typeNode = param.childForFieldName('type');
|
|
104
|
+
if (typeNode) {
|
|
105
|
+
return typeNode.type === 'pointer_type' ? typeNode.text.replace(/^\*/, '') : typeNode.text;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
110
111
|
function handleGoTypeDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
111
112
|
for (let i = 0; i < node.childCount; i++) {
|
|
112
113
|
const spec = node.child(i);
|
|
@@ -403,13 +404,38 @@ function extractGoParameters(paramListNode: TreeSitterNode | null): SubDeclarati
|
|
|
403
404
|
* This performs file-local matching (cross-file matching requires build-edges).
|
|
404
405
|
*/
|
|
405
406
|
function matchGoStructuralInterfaces(ctx: ExtractorOutput): void {
|
|
407
|
+
const { interfaceMethods, structMethods, structLines } = collectGoMethodSets(ctx);
|
|
408
|
+
|
|
409
|
+
// Match: struct satisfies interface if it has all interface methods (name-only;
|
|
410
|
+
// signatures are not verified — treat as candidate match, not definitive).
|
|
411
|
+
// NOTE: embedded interfaces (type_elem nodes) are not resolved — composite
|
|
412
|
+
// interfaces like `type ReadWriter interface { Reader; Writer }` will have an
|
|
413
|
+
// empty method set and be silently excluded from matching.
|
|
414
|
+
for (const [structName, methods] of structMethods) {
|
|
415
|
+
for (const [ifaceName, ifaceMethods] of interfaceMethods) {
|
|
416
|
+
if (ifaceMethods.size > 0 && [...ifaceMethods].every((m) => methods.has(m))) {
|
|
417
|
+
ctx.classes.push({
|
|
418
|
+
name: structName,
|
|
419
|
+
implements: ifaceName,
|
|
420
|
+
line: structLines.get(structName) || 1,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** Collect interface and struct method sets from definitions for structural matching. */
|
|
428
|
+
function collectGoMethodSets(ctx: ExtractorOutput): {
|
|
429
|
+
interfaceMethods: Map<string, Set<string>>;
|
|
430
|
+
structMethods: Map<string, Set<string>>;
|
|
431
|
+
structLines: Map<string, number>;
|
|
432
|
+
} {
|
|
406
433
|
const interfaceMethods = new Map<string, Set<string>>();
|
|
407
434
|
const structMethods = new Map<string, Set<string>>();
|
|
408
435
|
const structLines = new Map<string, number>();
|
|
409
|
-
|
|
410
|
-
// Collect interface and struct definitions
|
|
411
436
|
const interfaceNames = new Set<string>();
|
|
412
437
|
const structNames = new Set<string>();
|
|
438
|
+
|
|
413
439
|
for (const def of ctx.definitions) {
|
|
414
440
|
if (def.kind === 'interface') interfaceNames.add(def.name);
|
|
415
441
|
if (def.kind === 'struct') {
|
|
@@ -418,7 +444,6 @@ function matchGoStructuralInterfaces(ctx: ExtractorOutput): void {
|
|
|
418
444
|
}
|
|
419
445
|
}
|
|
420
446
|
|
|
421
|
-
// Collect methods grouped by receiver type
|
|
422
447
|
for (const def of ctx.definitions) {
|
|
423
448
|
if (def.kind !== 'method' || !def.name.includes('.')) continue;
|
|
424
449
|
const dotIdx = def.name.indexOf('.');
|
|
@@ -435,22 +460,7 @@ function matchGoStructuralInterfaces(ctx: ExtractorOutput): void {
|
|
|
435
460
|
}
|
|
436
461
|
}
|
|
437
462
|
|
|
438
|
-
|
|
439
|
-
// signatures are not verified — treat as candidate match, not definitive).
|
|
440
|
-
// NOTE: embedded interfaces (type_elem nodes) are not resolved — composite
|
|
441
|
-
// interfaces like `type ReadWriter interface { Reader; Writer }` will have an
|
|
442
|
-
// empty method set and be silently excluded from matching.
|
|
443
|
-
for (const [structName, methods] of structMethods) {
|
|
444
|
-
for (const [ifaceName, ifaceMethods] of interfaceMethods) {
|
|
445
|
-
if (ifaceMethods.size > 0 && [...ifaceMethods].every((m) => methods.has(m))) {
|
|
446
|
-
ctx.classes.push({
|
|
447
|
-
name: structName,
|
|
448
|
-
implements: ifaceName,
|
|
449
|
-
line: structLines.get(structName) || 1,
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
463
|
+
return { interfaceMethods, structMethods, structLines };
|
|
454
464
|
}
|
|
455
465
|
|
|
456
466
|
function extractStructFields(structTypeNode: TreeSitterNode): SubDeclaration[] {
|
|
@@ -110,6 +110,20 @@ export function findParentNode(
|
|
|
110
110
|
return null;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Resolve a container's body node by trying each field name in order.
|
|
115
|
+
*/
|
|
116
|
+
function resolveBodyNode(
|
|
117
|
+
containerNode: TreeSitterNode,
|
|
118
|
+
bodyFields: readonly string[],
|
|
119
|
+
): TreeSitterNode | null {
|
|
120
|
+
for (const field of bodyFields) {
|
|
121
|
+
const body = containerNode.childForFieldName(field) || findChild(containerNode, field);
|
|
122
|
+
if (body) return body;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
113
127
|
/**
|
|
114
128
|
* Extract child declarations from a container node's body.
|
|
115
129
|
* Finds the body via `bodyFields` (tries childForFieldName then findChild for each),
|
|
@@ -126,22 +140,17 @@ export function extractBodyMembers(
|
|
|
126
140
|
nameField: string = 'name',
|
|
127
141
|
visibility?: (member: TreeSitterNode) => SubDeclaration['visibility'],
|
|
128
142
|
): SubDeclaration[] {
|
|
143
|
+
const body = resolveBodyNode(containerNode, bodyFields);
|
|
144
|
+
if (!body) return [];
|
|
129
145
|
const members: SubDeclaration[] = [];
|
|
130
|
-
let body: TreeSitterNode | null = null;
|
|
131
|
-
for (const field of bodyFields) {
|
|
132
|
-
body = containerNode.childForFieldName(field) || findChild(containerNode, field);
|
|
133
|
-
if (body) break;
|
|
134
|
-
}
|
|
135
|
-
if (!body) return members;
|
|
136
146
|
for (let i = 0; i < body.childCount; i++) {
|
|
137
147
|
const member = body.child(i);
|
|
138
148
|
if (!member || member.type !== memberType) continue;
|
|
139
149
|
const nn = member.childForFieldName(nameField);
|
|
140
|
-
if (nn)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
150
|
+
if (!nn) continue;
|
|
151
|
+
const entry: SubDeclaration = { name: nn.text, kind, line: member.startPosition.row + 1 };
|
|
152
|
+
if (visibility) entry.visibility = visibility(member);
|
|
153
|
+
members.push(entry);
|
|
145
154
|
}
|
|
146
155
|
return members;
|
|
147
156
|
}
|
|
@@ -162,24 +171,29 @@ export function lastPathSegment(path: string, separator: string = '/'): string {
|
|
|
162
171
|
return path.split(separator).pop() ?? path;
|
|
163
172
|
}
|
|
164
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Parse visibility from a modifier node's text content.
|
|
176
|
+
*/
|
|
177
|
+
function parseModifierText(text: string): 'public' | 'private' | 'protected' | undefined {
|
|
178
|
+
if (VISIBILITY_KEYWORDS.has(text)) return text as 'public' | 'private' | 'protected';
|
|
179
|
+
// C# 'private protected' — accessible to derived types in same assembly → protected
|
|
180
|
+
if (text === 'private protected') return 'protected';
|
|
181
|
+
// Compound modifiers node (Java: "public static") — scan its text for a keyword
|
|
182
|
+
for (const kw of VISIBILITY_KEYWORDS) {
|
|
183
|
+
if (text.includes(kw)) return kw as 'public' | 'private' | 'protected';
|
|
184
|
+
}
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
165
188
|
export function extractModifierVisibility(
|
|
166
189
|
node: TreeSitterNode,
|
|
167
190
|
modifierTypes: Set<string> = DEFAULT_MODIFIER_TYPES,
|
|
168
191
|
): 'public' | 'private' | 'protected' | undefined {
|
|
169
192
|
for (let i = 0; i < node.childCount; i++) {
|
|
170
193
|
const child = node.child(i);
|
|
171
|
-
if (!child) continue;
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
const text = child.text;
|
|
175
|
-
if (VISIBILITY_KEYWORDS.has(text)) return text as 'public' | 'private' | 'protected';
|
|
176
|
-
// C# 'private protected' — accessible to derived types in same assembly → protected
|
|
177
|
-
if (text === 'private protected') return 'protected';
|
|
178
|
-
// Compound modifiers node (Java: "public static") — scan its text for a keyword
|
|
179
|
-
for (const kw of VISIBILITY_KEYWORDS) {
|
|
180
|
-
if (text.includes(kw)) return kw as 'public' | 'private' | 'protected';
|
|
181
|
-
}
|
|
182
|
-
}
|
|
194
|
+
if (!child || !modifierTypes.has(child.type)) continue;
|
|
195
|
+
const result = parseModifierText(child.text);
|
|
196
|
+
if (result) return result;
|
|
183
197
|
}
|
|
184
198
|
return undefined;
|
|
185
199
|
}
|
package/src/extractors/java.ts
CHANGED
|
@@ -83,27 +83,7 @@ function handleJavaClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
83
83
|
children: classChildren.length > 0 ? classChildren : undefined,
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
if (superclass) {
|
|
88
|
-
for (let i = 0; i < superclass.childCount; i++) {
|
|
89
|
-
const child = superclass.child(i);
|
|
90
|
-
if (
|
|
91
|
-
child &&
|
|
92
|
-
(child.type === 'type_identifier' ||
|
|
93
|
-
child.type === 'identifier' ||
|
|
94
|
-
child.type === 'generic_type')
|
|
95
|
-
) {
|
|
96
|
-
const superName = child.type === 'generic_type' ? child.child(0)?.text : child.text;
|
|
97
|
-
if (superName)
|
|
98
|
-
ctx.classes.push({
|
|
99
|
-
name: nameNode.text,
|
|
100
|
-
extends: superName,
|
|
101
|
-
line: node.startPosition.row + 1,
|
|
102
|
-
});
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
86
|
+
extractJavaSuperclass(node, nameNode.text, ctx);
|
|
107
87
|
|
|
108
88
|
const interfaces = node.childForFieldName('interfaces');
|
|
109
89
|
if (interfaces) {
|
|
@@ -111,6 +91,32 @@ function handleJavaClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
111
91
|
}
|
|
112
92
|
}
|
|
113
93
|
|
|
94
|
+
/** Extract the superclass (extends) relationship from a Java class declaration. */
|
|
95
|
+
function extractJavaSuperclass(
|
|
96
|
+
node: TreeSitterNode,
|
|
97
|
+
className: string,
|
|
98
|
+
ctx: ExtractorOutput,
|
|
99
|
+
): void {
|
|
100
|
+
const superclass = node.childForFieldName('superclass');
|
|
101
|
+
if (!superclass) return;
|
|
102
|
+
const superName = findJavaSuperTypeName(superclass);
|
|
103
|
+
if (superName) {
|
|
104
|
+
ctx.classes.push({ name: className, extends: superName, line: node.startPosition.row + 1 });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Find the type name from a superclass node (handles generic_type unwrapping). */
|
|
109
|
+
function findJavaSuperTypeName(superclass: TreeSitterNode): string | undefined {
|
|
110
|
+
for (let i = 0; i < superclass.childCount; i++) {
|
|
111
|
+
const child = superclass.child(i);
|
|
112
|
+
if (!child) continue;
|
|
113
|
+
if (JAVA_TYPE_NODE_TYPES.has(child.type)) {
|
|
114
|
+
return resolveJavaIfaceName(child);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
114
120
|
const JAVA_TYPE_NODE_TYPES = new Set(['type_identifier', 'identifier', 'generic_type']);
|
|
115
121
|
|
|
116
122
|
/** Resolve interface name from a type node (handles generic_type unwrapping). */
|
|
@@ -161,19 +167,26 @@ function handleJavaInterfaceDecl(node: TreeSitterNode, ctx: ExtractorOutput): vo
|
|
|
161
167
|
endLine: nodeEndLine(node),
|
|
162
168
|
});
|
|
163
169
|
const body = node.childForFieldName('body');
|
|
164
|
-
if (body)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
170
|
+
if (body) extractJavaInterfaceMethods(body, nameNode.text, ctx);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Extract method declarations from a Java interface body. */
|
|
174
|
+
function extractJavaInterfaceMethods(
|
|
175
|
+
body: TreeSitterNode,
|
|
176
|
+
ifaceName: string,
|
|
177
|
+
ctx: ExtractorOutput,
|
|
178
|
+
): void {
|
|
179
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
180
|
+
const child = body.child(i);
|
|
181
|
+
if (child && child.type === 'method_declaration') {
|
|
182
|
+
const methName = child.childForFieldName('name');
|
|
183
|
+
if (methName) {
|
|
184
|
+
ctx.definitions.push({
|
|
185
|
+
name: `${ifaceName}.${methName.text}`,
|
|
186
|
+
kind: 'method',
|
|
187
|
+
line: child.startPosition.row + 1,
|
|
188
|
+
endLine: child.endPosition.row + 1,
|
|
189
|
+
});
|
|
177
190
|
}
|
|
178
191
|
}
|
|
179
192
|
}
|
|
@@ -321,23 +334,29 @@ function extractClassFields(classNode: TreeSitterNode): SubDeclaration[] {
|
|
|
321
334
|
for (let i = 0; i < body.childCount; i++) {
|
|
322
335
|
const member = body.child(i);
|
|
323
336
|
if (!member || member.type !== 'field_declaration') continue;
|
|
324
|
-
|
|
325
|
-
const child = member.child(j);
|
|
326
|
-
if (!child || child.type !== 'variable_declarator') continue;
|
|
327
|
-
const nameNode = child.childForFieldName('name');
|
|
328
|
-
if (nameNode) {
|
|
329
|
-
fields.push({
|
|
330
|
-
name: nameNode.text,
|
|
331
|
-
kind: 'property',
|
|
332
|
-
line: member.startPosition.row + 1,
|
|
333
|
-
visibility: extractModifierVisibility(member),
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
+
extractFieldDeclarators(member, fields);
|
|
337
338
|
}
|
|
338
339
|
return fields;
|
|
339
340
|
}
|
|
340
341
|
|
|
342
|
+
/** Extract variable_declarator names from a field_declaration node. */
|
|
343
|
+
function extractFieldDeclarators(member: TreeSitterNode, fields: SubDeclaration[]): void {
|
|
344
|
+
const vis = extractModifierVisibility(member);
|
|
345
|
+
for (let j = 0; j < member.childCount; j++) {
|
|
346
|
+
const child = member.child(j);
|
|
347
|
+
if (!child || child.type !== 'variable_declarator') continue;
|
|
348
|
+
const nameNode = child.childForFieldName('name');
|
|
349
|
+
if (nameNode) {
|
|
350
|
+
fields.push({
|
|
351
|
+
name: nameNode.text,
|
|
352
|
+
kind: 'property',
|
|
353
|
+
line: member.startPosition.row + 1,
|
|
354
|
+
visibility: vis,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
341
360
|
function extractEnumConstants(enumNode: TreeSitterNode): SubDeclaration[] {
|
|
342
361
|
return extractBodyMembers(enumNode, ['body', 'enum_body'], 'enum_constant', 'constant');
|
|
343
362
|
}
|
|
@@ -328,34 +328,7 @@ function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]):
|
|
|
328
328
|
if (inner) declNode = inner;
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
|
|
332
|
-
if (t === 'lexical_declaration' || t === 'variable_declaration') {
|
|
333
|
-
if (declNode.text.startsWith('const ')) {
|
|
334
|
-
for (let j = 0; j < declNode.childCount; j++) {
|
|
335
|
-
const declarator = declNode.child(j);
|
|
336
|
-
if (!declarator || declarator.type !== 'variable_declarator') continue;
|
|
337
|
-
const nameN = declarator.childForFieldName('name');
|
|
338
|
-
const valueN = declarator.childForFieldName('value');
|
|
339
|
-
if (!nameN || nameN.type !== 'identifier' || !valueN) continue;
|
|
340
|
-
// Skip functions — already captured by query patterns
|
|
341
|
-
const valType = valueN.type;
|
|
342
|
-
if (
|
|
343
|
-
valType === 'arrow_function' ||
|
|
344
|
-
valType === 'function_expression' ||
|
|
345
|
-
valType === 'function'
|
|
346
|
-
)
|
|
347
|
-
continue;
|
|
348
|
-
if (isConstantValue(valueN)) {
|
|
349
|
-
definitions.push({
|
|
350
|
-
name: nameN.text,
|
|
351
|
-
kind: 'constant',
|
|
352
|
-
line: declNode.startPosition.row + 1,
|
|
353
|
-
endLine: nodeEndLine(declNode),
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
331
|
+
extractConstDeclarators(declNode, definitions);
|
|
359
332
|
|
|
360
333
|
// Recurse into non-function, non-export-statement children (blocks, if-statements, etc.)
|
|
361
334
|
if (child.type !== 'export_statement') {
|
|
@@ -364,6 +337,33 @@ function extractConstantsWalk(node: TreeSitterNode, definitions: Definition[]):
|
|
|
364
337
|
}
|
|
365
338
|
}
|
|
366
339
|
|
|
340
|
+
/** Extract constant definitions from a `const` declaration node. */
|
|
341
|
+
function extractConstDeclarators(declNode: TreeSitterNode, definitions: Definition[]): void {
|
|
342
|
+
const t = declNode.type;
|
|
343
|
+
if (t !== 'lexical_declaration' && t !== 'variable_declaration') return;
|
|
344
|
+
if (!declNode.text.startsWith('const ')) return;
|
|
345
|
+
|
|
346
|
+
for (let j = 0; j < declNode.childCount; j++) {
|
|
347
|
+
const declarator = declNode.child(j);
|
|
348
|
+
if (!declarator || declarator.type !== 'variable_declarator') continue;
|
|
349
|
+
const nameN = declarator.childForFieldName('name');
|
|
350
|
+
const valueN = declarator.childForFieldName('value');
|
|
351
|
+
if (!nameN || nameN.type !== 'identifier' || !valueN) continue;
|
|
352
|
+
// Skip functions — already captured by query patterns
|
|
353
|
+
const valType = valueN.type;
|
|
354
|
+
if (valType === 'arrow_function' || valType === 'function_expression' || valType === 'function')
|
|
355
|
+
continue;
|
|
356
|
+
if (isConstantValue(valueN)) {
|
|
357
|
+
definitions.push({
|
|
358
|
+
name: nameN.text,
|
|
359
|
+
kind: 'constant',
|
|
360
|
+
line: declNode.startPosition.row + 1,
|
|
361
|
+
endLine: nodeEndLine(declNode),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
367
|
/**
|
|
368
368
|
* Recursive walk to find dynamic import() calls.
|
|
369
369
|
* Query patterns match call_expression with identifier/member_expression/subscript_expression
|
|
@@ -678,24 +678,7 @@ function handleCallExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
678
678
|
const fn = node.childForFieldName('function');
|
|
679
679
|
if (!fn) return;
|
|
680
680
|
if (fn.type === 'import') {
|
|
681
|
-
|
|
682
|
-
if (args) {
|
|
683
|
-
const strArg = findChild(args, 'string');
|
|
684
|
-
if (strArg) {
|
|
685
|
-
const modPath = strArg.text.replace(/['"]/g, '');
|
|
686
|
-
const names = extractDynamicImportNames(node);
|
|
687
|
-
ctx.imports.push({
|
|
688
|
-
source: modPath,
|
|
689
|
-
names,
|
|
690
|
-
line: node.startPosition.row + 1,
|
|
691
|
-
dynamicImport: true,
|
|
692
|
-
});
|
|
693
|
-
} else {
|
|
694
|
-
debug(
|
|
695
|
-
`Skipping non-static dynamic import() at line ${node.startPosition.row + 1} (template literal or variable)`,
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
681
|
+
handleDynamicImportCall(node, ctx.imports);
|
|
699
682
|
} else {
|
|
700
683
|
const callInfo = extractCallInfo(fn, node);
|
|
701
684
|
if (callInfo) ctx.calls.push(callInfo);
|
|
@@ -706,6 +689,22 @@ function handleCallExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
|
706
689
|
}
|
|
707
690
|
}
|
|
708
691
|
|
|
692
|
+
/** Handle a dynamic import() call expression and add to imports if static. */
|
|
693
|
+
function handleDynamicImportCall(node: TreeSitterNode, imports: Import[]): void {
|
|
694
|
+
const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
|
|
695
|
+
if (!args) return;
|
|
696
|
+
const strArg = findChild(args, 'string');
|
|
697
|
+
if (strArg) {
|
|
698
|
+
const modPath = strArg.text.replace(/['"]/g, '');
|
|
699
|
+
const names = extractDynamicImportNames(node);
|
|
700
|
+
imports.push({ source: modPath, names, line: node.startPosition.row + 1, dynamicImport: true });
|
|
701
|
+
} else {
|
|
702
|
+
debug(
|
|
703
|
+
`Skipping non-static dynamic import() at line ${node.startPosition.row + 1} (template literal or variable)`,
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
709
708
|
function handleImportStmt(node: TreeSitterNode, ctx: ExtractorOutput): void {
|
|
710
709
|
const isTypeOnly = node.text.startsWith('import type');
|
|
711
710
|
const source = node.childForFieldName('source') || findChild(node, 'string');
|
package/src/extractors/kotlin.ts
CHANGED
|
@@ -81,48 +81,7 @@ function handleKotlinClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void
|
|
|
81
81
|
|
|
82
82
|
const kind = isInterface ? 'interface' : isEnum ? 'enum' : 'class';
|
|
83
83
|
|
|
84
|
-
const children
|
|
85
|
-
if (isEnum) {
|
|
86
|
-
// Enum entries are inside class_body
|
|
87
|
-
const body = findChild(node, 'class_body');
|
|
88
|
-
if (body) {
|
|
89
|
-
for (let i = 0; i < body.childCount; i++) {
|
|
90
|
-
const child = body.child(i);
|
|
91
|
-
if (child && child.type === 'enum_entry') {
|
|
92
|
-
const entryName = findChild(child, 'simple_identifier');
|
|
93
|
-
if (entryName) {
|
|
94
|
-
children.push({
|
|
95
|
-
name: entryName.text,
|
|
96
|
-
kind: 'constant',
|
|
97
|
-
line: child.startPosition.row + 1,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
// Extract properties from class_body
|
|
105
|
-
const body = findChild(node, 'class_body');
|
|
106
|
-
if (body) {
|
|
107
|
-
for (let i = 0; i < body.childCount; i++) {
|
|
108
|
-
const child = body.child(i);
|
|
109
|
-
if (child && child.type === 'property_declaration') {
|
|
110
|
-
const propName = findChild(child, 'variable_declaration');
|
|
111
|
-
if (propName) {
|
|
112
|
-
const id = findChild(propName, 'simple_identifier');
|
|
113
|
-
if (id) {
|
|
114
|
-
children.push({
|
|
115
|
-
name: id.text,
|
|
116
|
-
kind: 'property',
|
|
117
|
-
line: child.startPosition.row + 1,
|
|
118
|
-
visibility: extractModifierVisibility(child),
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
84
|
+
const children = isEnum ? collectKotlinEnumEntries(node) : collectKotlinProperties(node);
|
|
126
85
|
|
|
127
86
|
ctx.definitions.push({
|
|
128
87
|
name,
|
|
@@ -132,27 +91,79 @@ function handleKotlinClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void
|
|
|
132
91
|
children: children.length > 0 ? children : undefined,
|
|
133
92
|
});
|
|
134
93
|
|
|
135
|
-
|
|
94
|
+
collectKotlinMethods(node, name, ctx);
|
|
95
|
+
collectKotlinInheritance(node, name, ctx);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Collect enum constant entries from a class_body. */
|
|
99
|
+
function collectKotlinEnumEntries(node: TreeSitterNode): SubDeclaration[] {
|
|
100
|
+
const entries: SubDeclaration[] = [];
|
|
136
101
|
const body = findChild(node, 'class_body');
|
|
137
|
-
if (body)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
visibility: extractModifierVisibility(child),
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}
|
|
102
|
+
if (!body) return entries;
|
|
103
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
104
|
+
const child = body.child(i);
|
|
105
|
+
if (!child || child.type !== 'enum_entry') continue;
|
|
106
|
+
const entryName = findChild(child, 'simple_identifier');
|
|
107
|
+
if (entryName) {
|
|
108
|
+
entries.push({
|
|
109
|
+
name: entryName.text,
|
|
110
|
+
kind: 'constant',
|
|
111
|
+
line: child.startPosition.row + 1,
|
|
112
|
+
});
|
|
152
113
|
}
|
|
153
114
|
}
|
|
115
|
+
return entries;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Collect property declarations from a class_body. */
|
|
119
|
+
function collectKotlinProperties(node: TreeSitterNode): SubDeclaration[] {
|
|
120
|
+
const props: SubDeclaration[] = [];
|
|
121
|
+
const body = findChild(node, 'class_body');
|
|
122
|
+
if (!body) return props;
|
|
123
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
124
|
+
const child = body.child(i);
|
|
125
|
+
if (!child || child.type !== 'property_declaration') continue;
|
|
126
|
+
const varDecl = findChild(child, 'variable_declaration');
|
|
127
|
+
if (!varDecl) continue;
|
|
128
|
+
const id = findChild(varDecl, 'simple_identifier');
|
|
129
|
+
if (id) {
|
|
130
|
+
props.push({
|
|
131
|
+
name: id.text,
|
|
132
|
+
kind: 'property',
|
|
133
|
+
line: child.startPosition.row + 1,
|
|
134
|
+
visibility: extractModifierVisibility(child),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return props;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Collect method declarations from a class_body. */
|
|
142
|
+
function collectKotlinMethods(node: TreeSitterNode, className: string, ctx: ExtractorOutput): void {
|
|
143
|
+
const body = findChild(node, 'class_body');
|
|
144
|
+
if (!body) return;
|
|
145
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
146
|
+
const child = body.child(i);
|
|
147
|
+
if (!child || child.type !== 'function_declaration') continue;
|
|
148
|
+
const methName = findChild(child, 'simple_identifier');
|
|
149
|
+
if (methName) {
|
|
150
|
+
ctx.definitions.push({
|
|
151
|
+
name: `${className}.${methName.text}`,
|
|
152
|
+
kind: 'method',
|
|
153
|
+
line: child.startPosition.row + 1,
|
|
154
|
+
endLine: child.endPosition.row + 1,
|
|
155
|
+
visibility: extractModifierVisibility(child),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
154
160
|
|
|
155
|
-
|
|
161
|
+
/** Collect inheritance relationships from delegation_specifier children. */
|
|
162
|
+
function collectKotlinInheritance(
|
|
163
|
+
node: TreeSitterNode,
|
|
164
|
+
className: string,
|
|
165
|
+
ctx: ExtractorOutput,
|
|
166
|
+
): void {
|
|
156
167
|
for (let i = 0; i < node.childCount; i++) {
|
|
157
168
|
const child = node.child(i);
|
|
158
169
|
if (!child || child.type !== 'delegation_specifier') continue;
|
|
@@ -161,30 +172,26 @@ function handleKotlinClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void
|
|
|
161
172
|
const ctorInvocation = findChild(child, 'constructor_invocation');
|
|
162
173
|
if (ctorInvocation) {
|
|
163
174
|
const userType = findChild(ctorInvocation, 'user_type');
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
});
|
|
172
|
-
}
|
|
175
|
+
const typeId = userType ? findChild(userType, 'type_identifier') : null;
|
|
176
|
+
if (typeId) {
|
|
177
|
+
ctx.classes.push({
|
|
178
|
+
name: className,
|
|
179
|
+
extends: typeId.text,
|
|
180
|
+
line: node.startPosition.row + 1,
|
|
181
|
+
});
|
|
173
182
|
}
|
|
174
183
|
continue;
|
|
175
184
|
}
|
|
176
185
|
|
|
177
186
|
// user_type > type_identifier (implements)
|
|
178
187
|
const userType = findChild(child, 'user_type');
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
+
const typeId = userType ? findChild(userType, 'type_identifier') : null;
|
|
189
|
+
if (typeId) {
|
|
190
|
+
ctx.classes.push({
|
|
191
|
+
name: className,
|
|
192
|
+
implements: typeId.text,
|
|
193
|
+
line: node.startPosition.row + 1,
|
|
194
|
+
});
|
|
188
195
|
}
|
|
189
196
|
}
|
|
190
197
|
}
|