gitnexus 1.5.3 → 1.6.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 +10 -0
- package/dist/_shared/graph/types.d.ts +1 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +1 -0
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +2 -0
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/languages.d.ts +1 -0
- package/dist/_shared/languages.d.ts.map +1 -1
- package/dist/_shared/languages.js +1 -0
- package/dist/_shared/languages.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +3 -1
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/mro-strategy.d.ts +19 -0
- package/dist/_shared/mro-strategy.d.ts.map +1 -0
- package/dist/_shared/mro-strategy.js +2 -0
- package/dist/_shared/mro-strategy.js.map +1 -0
- package/dist/cli/ai-context.d.ts +1 -0
- package/dist/cli/ai-context.js +28 -4
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +30 -4
- package/dist/cli/group.d.ts +2 -0
- package/dist/cli/group.js +233 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/serve.js +4 -1
- package/dist/cli/setup.js +34 -3
- package/dist/config/ignore-service.js +8 -3
- package/dist/core/augmentation/engine.js +1 -1
- package/dist/core/git-staleness.d.ts +13 -0
- package/dist/core/git-staleness.js +29 -0
- package/dist/core/group/bridge-db.d.ts +82 -0
- package/dist/core/group/bridge-db.js +460 -0
- package/dist/core/group/bridge-schema.d.ts +27 -0
- package/dist/core/group/bridge-schema.js +55 -0
- package/dist/core/group/config-parser.d.ts +3 -0
- package/dist/core/group/config-parser.js +83 -0
- package/dist/core/group/contract-extractor.d.ts +7 -0
- package/dist/core/group/contract-extractor.js +1 -0
- package/dist/core/group/extractors/fs-utils.d.ts +10 -0
- package/dist/core/group/extractors/fs-utils.js +24 -0
- package/dist/core/group/extractors/grpc-extractor.d.ts +25 -0
- package/dist/core/group/extractors/grpc-extractor.js +386 -0
- package/dist/core/group/extractors/grpc-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/go.js +97 -0
- package/dist/core/group/extractors/grpc-patterns/index.d.ts +19 -0
- package/dist/core/group/extractors/grpc-patterns/index.js +46 -0
- package/dist/core/group/extractors/grpc-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/java.js +173 -0
- package/dist/core/group/extractors/grpc-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/grpc-patterns/node.js +290 -0
- package/dist/core/group/extractors/grpc-patterns/proto.d.ts +9 -0
- package/dist/core/group/extractors/grpc-patterns/proto.js +134 -0
- package/dist/core/group/extractors/grpc-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/grpc-patterns/python.js +67 -0
- package/dist/core/group/extractors/grpc-patterns/types.d.ts +50 -0
- package/dist/core/group/extractors/grpc-patterns/types.js +1 -0
- package/dist/core/group/extractors/http-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/go.js +215 -0
- package/dist/core/group/extractors/http-patterns/index.d.ts +17 -0
- package/dist/core/group/extractors/http-patterns/index.js +44 -0
- package/dist/core/group/extractors/http-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/java.js +253 -0
- package/dist/core/group/extractors/http-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/http-patterns/node.js +354 -0
- package/dist/core/group/extractors/http-patterns/php.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/php.js +70 -0
- package/dist/core/group/extractors/http-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/http-patterns/python.js +133 -0
- package/dist/core/group/extractors/http-patterns/types.d.ts +61 -0
- package/dist/core/group/extractors/http-patterns/types.js +1 -0
- package/dist/core/group/extractors/http-route-extractor.d.ts +21 -0
- package/dist/core/group/extractors/http-route-extractor.js +391 -0
- package/dist/core/group/extractors/manifest-extractor.d.ts +54 -0
- package/dist/core/group/extractors/manifest-extractor.js +235 -0
- package/dist/core/group/extractors/topic-extractor.d.ts +8 -0
- package/dist/core/group/extractors/topic-extractor.js +97 -0
- package/dist/core/group/extractors/topic-patterns/go.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/go.js +120 -0
- package/dist/core/group/extractors/topic-patterns/index.d.ts +14 -0
- package/dist/core/group/extractors/topic-patterns/index.js +38 -0
- package/dist/core/group/extractors/topic-patterns/java.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/java.js +80 -0
- package/dist/core/group/extractors/topic-patterns/node.d.ts +4 -0
- package/dist/core/group/extractors/topic-patterns/node.js +155 -0
- package/dist/core/group/extractors/topic-patterns/python.d.ts +2 -0
- package/dist/core/group/extractors/topic-patterns/python.js +116 -0
- package/dist/core/group/extractors/topic-patterns/types.d.ts +25 -0
- package/dist/core/group/extractors/topic-patterns/types.js +10 -0
- package/dist/core/group/extractors/tree-sitter-scanner.d.ts +113 -0
- package/dist/core/group/extractors/tree-sitter-scanner.js +94 -0
- package/dist/core/group/matching.d.ts +13 -0
- package/dist/core/group/matching.js +198 -0
- package/dist/core/group/normalization.d.ts +3 -0
- package/dist/core/group/normalization.js +115 -0
- package/dist/core/group/service-boundary-detector.d.ts +8 -0
- package/dist/core/group/service-boundary-detector.js +155 -0
- package/dist/core/group/service.d.ts +46 -0
- package/dist/core/group/service.js +160 -0
- package/dist/core/group/storage.d.ts +9 -0
- package/dist/core/group/storage.js +91 -0
- package/dist/core/group/sync.d.ts +21 -0
- package/dist/core/group/sync.js +148 -0
- package/dist/core/group/types.d.ts +130 -0
- package/dist/core/group/types.js +1 -0
- package/dist/core/ingestion/binding-accumulator.d.ts +212 -0
- package/dist/core/ingestion/binding-accumulator.js +336 -0
- package/dist/core/ingestion/call-processor.d.ts +155 -24
- package/dist/core/ingestion/call-processor.js +1129 -247
- package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
- package/dist/core/ingestion/class-extractors/generic.js +135 -0
- package/dist/core/ingestion/class-types.d.ts +34 -0
- package/dist/core/ingestion/class-types.js +1 -0
- package/dist/core/ingestion/cobol-processor.d.ts +1 -1
- package/dist/core/ingestion/entry-point-scoring.d.ts +1 -0
- package/dist/core/ingestion/entry-point-scoring.js +1 -0
- package/dist/core/ingestion/field-types.d.ts +2 -2
- package/dist/core/ingestion/filesystem-walker.js +8 -0
- package/dist/core/ingestion/framework-detection.d.ts +1 -0
- package/dist/core/ingestion/framework-detection.js +1 -0
- package/dist/core/ingestion/heritage-processor.d.ts +8 -15
- package/dist/core/ingestion/heritage-processor.js +15 -28
- package/dist/core/ingestion/import-processor.d.ts +1 -11
- package/dist/core/ingestion/import-processor.js +1 -13
- package/dist/core/ingestion/import-resolvers/utils.js +1 -0
- package/dist/core/ingestion/import-resolvers/vue.d.ts +8 -0
- package/dist/core/ingestion/import-resolvers/vue.js +9 -0
- package/dist/core/ingestion/language-config.js +1 -1
- package/dist/core/ingestion/language-provider.d.ts +14 -3
- package/dist/core/ingestion/languages/c-cpp.js +168 -1
- package/dist/core/ingestion/languages/csharp.js +20 -0
- package/dist/core/ingestion/languages/dart.js +26 -4
- package/dist/core/ingestion/languages/go.js +22 -0
- package/dist/core/ingestion/languages/index.d.ts +1 -0
- package/dist/core/ingestion/languages/index.js +2 -0
- package/dist/core/ingestion/languages/java.js +17 -0
- package/dist/core/ingestion/languages/kotlin.js +24 -1
- package/dist/core/ingestion/languages/php.js +23 -11
- package/dist/core/ingestion/languages/python.js +9 -0
- package/dist/core/ingestion/languages/ruby.js +43 -0
- package/dist/core/ingestion/languages/rust.js +38 -0
- package/dist/core/ingestion/languages/swift.js +31 -0
- package/dist/core/ingestion/languages/typescript.d.ts +1 -0
- package/dist/core/ingestion/languages/typescript.js +52 -3
- package/dist/core/ingestion/languages/vue.d.ts +13 -0
- package/dist/core/ingestion/languages/vue.js +81 -0
- package/dist/core/ingestion/markdown-processor.d.ts +1 -1
- package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
- package/dist/core/ingestion/method-extractors/configs/csharp.js +5 -1
- package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
- package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.js +14 -4
- package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
- package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/ruby.js +286 -0
- package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
- package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
- package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +85 -8
- package/dist/core/ingestion/method-extractors/generic.d.ts +6 -0
- package/dist/core/ingestion/method-extractors/generic.js +84 -17
- package/dist/core/ingestion/method-types.d.ts +29 -0
- package/dist/core/ingestion/model/field-registry.d.ts +18 -0
- package/dist/core/ingestion/model/field-registry.js +22 -0
- package/dist/core/ingestion/model/heritage-map.d.ts +70 -0
- package/dist/core/ingestion/model/heritage-map.js +159 -0
- package/dist/core/ingestion/model/index.d.ts +20 -0
- package/dist/core/ingestion/model/index.js +41 -0
- package/dist/core/ingestion/model/method-registry.d.ts +62 -0
- package/dist/core/ingestion/model/method-registry.js +130 -0
- package/dist/core/ingestion/model/registration-table.d.ts +139 -0
- package/dist/core/ingestion/model/registration-table.js +224 -0
- package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
- package/dist/core/ingestion/model/resolution-context.js +337 -0
- package/dist/core/ingestion/model/resolve.d.ts +56 -0
- package/dist/core/ingestion/model/resolve.js +297 -0
- package/dist/core/ingestion/model/semantic-model.d.ts +86 -0
- package/dist/core/ingestion/model/semantic-model.js +120 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +222 -0
- package/dist/core/ingestion/model/symbol-table.js +206 -0
- package/dist/core/ingestion/model/type-registry.d.ts +39 -0
- package/dist/core/ingestion/model/type-registry.js +62 -0
- package/dist/core/ingestion/mro-processor.d.ts +5 -4
- package/dist/core/ingestion/mro-processor.js +311 -107
- package/dist/core/ingestion/parsing-processor.d.ts +5 -4
- package/dist/core/ingestion/parsing-processor.js +224 -87
- package/dist/core/ingestion/pipeline-phases/cobol.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/cobol.js +45 -0
- package/dist/core/ingestion/pipeline-phases/communities.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/communities.js +62 -0
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/cross-file-impl.js +156 -0
- package/dist/core/ingestion/pipeline-phases/cross-file.d.ts +37 -0
- package/dist/core/ingestion/pipeline-phases/cross-file.js +63 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/index.js +22 -0
- package/dist/core/ingestion/pipeline-phases/markdown.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/markdown.js +33 -0
- package/dist/core/ingestion/pipeline-phases/mro.d.ts +18 -0
- package/dist/core/ingestion/pipeline-phases/mro.js +36 -0
- package/dist/core/ingestion/pipeline-phases/orm-extraction.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/orm-extraction.js +92 -0
- package/dist/core/ingestion/pipeline-phases/orm.d.ts +15 -0
- package/dist/core/ingestion/pipeline-phases/orm.js +74 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +47 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +437 -0
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +49 -0
- package/dist/core/ingestion/pipeline-phases/parse.js +33 -0
- package/dist/core/ingestion/pipeline-phases/processes.d.ts +16 -0
- package/dist/core/ingestion/pipeline-phases/processes.js +143 -0
- package/dist/core/ingestion/pipeline-phases/routes.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +243 -0
- package/dist/core/ingestion/pipeline-phases/runner.d.ts +22 -0
- package/dist/core/ingestion/pipeline-phases/runner.js +203 -0
- package/dist/core/ingestion/pipeline-phases/scan.d.ts +21 -0
- package/dist/core/ingestion/pipeline-phases/scan.js +46 -0
- package/dist/core/ingestion/pipeline-phases/structure.d.ts +27 -0
- package/dist/core/ingestion/pipeline-phases/structure.js +35 -0
- package/dist/core/ingestion/pipeline-phases/tools.d.ts +20 -0
- package/dist/core/ingestion/pipeline-phases/tools.js +79 -0
- package/dist/core/ingestion/pipeline-phases/types.d.ts +79 -0
- package/dist/core/ingestion/pipeline-phases/types.js +37 -0
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.d.ts +35 -0
- package/dist/core/ingestion/pipeline-phases/wildcard-synthesis.js +174 -0
- package/dist/core/ingestion/pipeline.d.ts +18 -10
- package/dist/core/ingestion/pipeline.js +66 -1410
- package/dist/core/ingestion/process-processor.js +1 -1
- package/dist/core/ingestion/tree-sitter-queries.d.ts +5 -5
- package/dist/core/ingestion/tree-sitter-queries.js +90 -0
- package/dist/core/ingestion/type-env.d.ts +15 -2
- package/dist/core/ingestion/type-env.js +163 -102
- package/dist/core/ingestion/type-extractors/csharp.js +17 -0
- package/dist/core/ingestion/type-extractors/jvm.js +11 -0
- package/dist/core/ingestion/type-extractors/php.js +0 -55
- package/dist/core/ingestion/type-extractors/ruby.js +0 -32
- package/dist/core/ingestion/type-extractors/swift.js +13 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +8 -8
- package/dist/core/ingestion/type-extractors/typescript.js +66 -69
- package/dist/core/ingestion/utils/ast-helpers.d.ts +32 -44
- package/dist/core/ingestion/utils/ast-helpers.js +157 -573
- package/dist/core/ingestion/utils/env.d.ts +10 -0
- package/dist/core/ingestion/utils/env.js +10 -0
- package/dist/core/ingestion/utils/graph-sort.d.ts +58 -0
- package/dist/core/ingestion/utils/graph-sort.js +100 -0
- package/dist/core/ingestion/utils/method-props.d.ts +32 -0
- package/dist/core/ingestion/utils/method-props.js +147 -0
- package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
- package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +31 -19
- package/dist/core/ingestion/workers/parse-worker.js +469 -200
- package/dist/core/lbug/lbug-adapter.d.ts +6 -0
- package/dist/core/lbug/lbug-adapter.js +134 -27
- package/dist/core/lbug/pool-adapter.d.ts +76 -0
- package/dist/core/lbug/pool-adapter.js +522 -0
- package/dist/core/run-analyze.d.ts +2 -0
- package/dist/core/run-analyze.js +1 -1
- package/dist/core/search/bm25-index.js +1 -1
- package/dist/core/tree-sitter/parser-loader.js +1 -0
- package/dist/core/wiki/graph-queries.js +1 -1
- package/dist/mcp/core/embedder.js +6 -5
- package/dist/mcp/core/lbug-adapter.d.ts +3 -63
- package/dist/mcp/core/lbug-adapter.js +3 -484
- package/dist/mcp/local/local-backend.d.ts +31 -2
- package/dist/mcp/local/local-backend.js +255 -46
- package/dist/mcp/resources.js +5 -4
- package/dist/mcp/staleness.d.ts +3 -13
- package/dist/mcp/staleness.js +2 -31
- package/dist/mcp/tools.js +80 -4
- package/dist/server/analyze-job.d.ts +2 -0
- package/dist/server/analyze-job.js +4 -0
- package/dist/server/api.d.ts +20 -1
- package/dist/server/api.js +306 -71
- package/dist/server/git-clone.d.ts +2 -1
- package/dist/server/git-clone.js +98 -5
- package/dist/storage/git.d.ts +13 -0
- package/dist/storage/git.js +25 -0
- package/dist/storage/repo-manager.js +1 -1
- package/package.json +9 -3
- package/scripts/patch-tree-sitter-swift.cjs +78 -0
- package/vendor/tree-sitter-proto/binding.gyp +30 -0
- package/vendor/tree-sitter-proto/bindings/node/binding.cc +20 -0
- package/vendor/tree-sitter-proto/bindings/node/index.d.ts +28 -0
- package/vendor/tree-sitter-proto/bindings/node/index.js +7 -0
- package/vendor/tree-sitter-proto/package.json +18 -0
- package/vendor/tree-sitter-proto/src/node-types.json +1145 -0
- package/vendor/tree-sitter-proto/src/parser.c +10149 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/array.h +291 -0
- package/vendor/tree-sitter-proto/src/tree_sitter/parser.h +266 -0
- package/dist/core/ingestion/named-binding-processor.d.ts +0 -18
- package/dist/core/ingestion/named-binding-processor.js +0 -42
- package/dist/core/ingestion/resolution-context.d.ts +0 -58
- package/dist/core/ingestion/resolution-context.js +0 -135
- package/dist/core/ingestion/symbol-table.d.ts +0 -79
- package/dist/core/ingestion/symbol-table.js +0 -115
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { generateId } from '../../../lib/utils.js';
|
|
2
|
-
import { extractSimpleTypeName } from '../type-extractors/shared.js';
|
|
3
2
|
/**
|
|
4
3
|
* Ordered list of definition capture keys for tree-sitter query matches.
|
|
5
4
|
* Used to extract the definition node from a capture map.
|
|
@@ -38,7 +37,13 @@ export const getDefinitionNodeFromCaptures = (captureMap) => {
|
|
|
38
37
|
};
|
|
39
38
|
/**
|
|
40
39
|
* Node types that represent function/method definitions across languages.
|
|
41
|
-
* Used
|
|
40
|
+
* Used by parent-walk in call-processor, parse-worker, and type-env to detect
|
|
41
|
+
* enclosing function scope boundaries.
|
|
42
|
+
*
|
|
43
|
+
* INVARIANT: This set MUST be a superset of every language's
|
|
44
|
+
* MethodExtractionConfig.methodNodeTypes. When adding a new node type to a
|
|
45
|
+
* MethodExtractor config, add it here too — otherwise enclosing-function
|
|
46
|
+
* resolution will silently miss that node type during parent-walks.
|
|
42
47
|
*/
|
|
43
48
|
export const FUNCTION_NODE_TYPES = new Set([
|
|
44
49
|
// TypeScript/JavaScript
|
|
@@ -81,17 +86,13 @@ export const FUNCTION_NODE_TYPES = new Set([
|
|
|
81
86
|
'method_signature',
|
|
82
87
|
]);
|
|
83
88
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
89
|
+
* AST node types that represent a class-like container (for HAS_METHOD edge extraction).
|
|
90
|
+
*
|
|
91
|
+
* INVARIANT: When a language config adds a new node type to `typeDeclarationNodes`,
|
|
92
|
+
* that type must also be added here AND to `CONTAINER_TYPE_TO_LABEL` below,
|
|
93
|
+
* otherwise `findEnclosingClassNode` won't recognize it and methods may get
|
|
94
|
+
* orphaned HAS_METHOD edges or incorrect labels.
|
|
86
95
|
*/
|
|
87
|
-
export const FUNCTION_DECLARATION_TYPES = new Set([
|
|
88
|
-
'function_declaration',
|
|
89
|
-
'function_definition',
|
|
90
|
-
'async_function_declaration',
|
|
91
|
-
'generator_function_declaration',
|
|
92
|
-
'function_item',
|
|
93
|
-
]);
|
|
94
|
-
/** AST node types that represent a class-like container (for HAS_METHOD edge extraction) */
|
|
95
96
|
export const CLASS_CONTAINER_TYPES = new Set([
|
|
96
97
|
'class_declaration',
|
|
97
98
|
'abstract_class_declaration',
|
|
@@ -106,10 +107,16 @@ export const CLASS_CONTAINER_TYPES = new Set([
|
|
|
106
107
|
'enum_item',
|
|
107
108
|
'class_definition',
|
|
108
109
|
'trait_declaration',
|
|
110
|
+
// PHP
|
|
111
|
+
'enum_declaration',
|
|
109
112
|
'protocol_declaration',
|
|
113
|
+
// Dart
|
|
114
|
+
'mixin_declaration',
|
|
115
|
+
'extension_declaration',
|
|
110
116
|
// Ruby
|
|
111
117
|
'class',
|
|
112
118
|
'module',
|
|
119
|
+
'singleton_class', // Ruby: class << self
|
|
113
120
|
// Kotlin
|
|
114
121
|
'object_declaration',
|
|
115
122
|
'companion_object',
|
|
@@ -127,25 +134,17 @@ export const CONTAINER_TYPE_TO_LABEL = {
|
|
|
127
134
|
struct_item: 'Struct',
|
|
128
135
|
enum_item: 'Enum',
|
|
129
136
|
trait_declaration: 'Trait',
|
|
137
|
+
enum_declaration: 'Enum',
|
|
130
138
|
record_declaration: 'Record',
|
|
131
139
|
protocol_declaration: 'Interface',
|
|
140
|
+
mixin_declaration: 'Mixin',
|
|
141
|
+
extension_declaration: 'Extension',
|
|
132
142
|
class: 'Class',
|
|
133
143
|
module: 'Module',
|
|
144
|
+
singleton_class: 'Class', // Ruby: class << self inherits enclosing class name
|
|
134
145
|
object_declaration: 'Class',
|
|
135
146
|
companion_object: 'Class',
|
|
136
147
|
};
|
|
137
|
-
/** Check if a Kotlin function_declaration capture is inside a class_body (i.e., a method).
|
|
138
|
-
* Kotlin grammar uses function_declaration for both top-level functions and class methods.
|
|
139
|
-
* Returns true when the captured definition node has a class_body ancestor. */
|
|
140
|
-
export function isKotlinClassMethod(captureNode) {
|
|
141
|
-
let ancestor = captureNode?.parent;
|
|
142
|
-
while (ancestor) {
|
|
143
|
-
if (ancestor.type === 'class_body')
|
|
144
|
-
return true;
|
|
145
|
-
ancestor = ancestor.parent;
|
|
146
|
-
}
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
148
|
/**
|
|
150
149
|
* Determine the graph node label from a tree-sitter capture map.
|
|
151
150
|
* Handles language-specific reclassification via the provider's labelOverride hook
|
|
@@ -209,31 +208,55 @@ export function getLabelFromCaptures(captureMap, provider) {
|
|
|
209
208
|
return 'Template';
|
|
210
209
|
return 'CodeElement';
|
|
211
210
|
}
|
|
212
|
-
/** Walk up AST to find enclosing class/struct/interface/impl, return its
|
|
213
|
-
* For Go method_declaration nodes, extracts receiver type (e.g. `func (u *User) Save()` → User struct).
|
|
214
|
-
|
|
211
|
+
/** Walk up AST to find enclosing class/struct/interface/impl, return its ID and name.
|
|
212
|
+
* For Go method_declaration nodes, extracts receiver type (e.g. `func (u *User) Save()` → User struct).
|
|
213
|
+
*
|
|
214
|
+
* @param resolveEnclosingOwner Optional language-specific hook for container remapping.
|
|
215
|
+
* When provided and a CLASS_CONTAINER_TYPES node is found, this hook is called:
|
|
216
|
+
* - Return a different SyntaxNode to remap the container (e.g., Ruby singleton_class → class).
|
|
217
|
+
* - Return `null` to skip this container and keep walking up.
|
|
218
|
+
* - Return the input node (identity) to use the container as-is.
|
|
219
|
+
* When omitted, the container node is used as-is.
|
|
220
|
+
*
|
|
221
|
+
* INVARIANT: Implementers SHOULD return either `null`, the input node, or
|
|
222
|
+
* another CLASS_CONTAINER_TYPES node. Returning a non-container node is
|
|
223
|
+
* permitted but discouraged — it will cause the walk to skip the current
|
|
224
|
+
* container and continue from the redirected node's parent. The
|
|
225
|
+
* `MAX_ENCLOSING_WALK_ITERATIONS` defense-in-depth guard below prevents
|
|
226
|
+
* pathological hooks from creating an infinite loop. */
|
|
227
|
+
const MAX_ENCLOSING_WALK_ITERATIONS = 4096;
|
|
228
|
+
export const findEnclosingClassInfo = (node, filePath, resolveEnclosingOwner) => {
|
|
215
229
|
let current = node.parent;
|
|
230
|
+
let iterations = 0;
|
|
231
|
+
// Tracks container nodes already visited via the hook so a misbehaving hook
|
|
232
|
+
// that keeps redirecting back to the same container cannot loop forever.
|
|
233
|
+
const visitedContainers = new Set();
|
|
216
234
|
while (current) {
|
|
235
|
+
if (++iterations > MAX_ENCLOSING_WALK_ITERATIONS) {
|
|
236
|
+
// Defense-in-depth: a real source tree has nowhere near this many ancestors.
|
|
237
|
+
// Bail out rather than hang ingestion.
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
217
240
|
// Go: method_declaration has a receiver parameter with the struct type
|
|
218
241
|
if (current.type === 'method_declaration') {
|
|
219
242
|
const receiver = current.childForFieldName?.('receiver');
|
|
220
243
|
if (receiver) {
|
|
221
|
-
// receiver is a parameter_list: (u *User) or (u User)
|
|
222
244
|
const paramDecl = receiver.namedChildren?.find?.((c) => c.type === 'parameter_declaration');
|
|
223
245
|
if (paramDecl) {
|
|
224
246
|
const typeNode = paramDecl.childForFieldName?.('type');
|
|
225
247
|
if (typeNode) {
|
|
226
|
-
// Unwrap pointer_type (*User → User)
|
|
227
248
|
const inner = typeNode.type === 'pointer_type' ? typeNode.firstNamedChild : typeNode;
|
|
228
249
|
if (inner && (inner.type === 'type_identifier' || inner.type === 'identifier')) {
|
|
229
|
-
return
|
|
250
|
+
return {
|
|
251
|
+
classId: generateId('Struct', `${filePath}:${inner.text}`),
|
|
252
|
+
className: inner.text,
|
|
253
|
+
};
|
|
230
254
|
}
|
|
231
255
|
}
|
|
232
256
|
}
|
|
233
257
|
}
|
|
234
258
|
}
|
|
235
259
|
// Go: type_declaration wrapping a struct_type (type User struct { ... })
|
|
236
|
-
// field_declaration → field_declaration_list → struct_type → type_spec → type_declaration
|
|
237
260
|
if (current.type === 'type_declaration') {
|
|
238
261
|
const typeSpec = current.children?.find((c) => c.type === 'type_spec');
|
|
239
262
|
if (typeSpec) {
|
|
@@ -242,25 +265,63 @@ export const findEnclosingClassId = (node, filePath) => {
|
|
|
242
265
|
const nameNode = typeSpec.childForFieldName?.('name');
|
|
243
266
|
if (nameNode) {
|
|
244
267
|
const label = typeBody.type === 'struct_type' ? 'Struct' : 'Interface';
|
|
245
|
-
return
|
|
268
|
+
return {
|
|
269
|
+
classId: generateId(label, `${filePath}:${nameNode.text}`),
|
|
270
|
+
className: nameNode.text,
|
|
271
|
+
};
|
|
246
272
|
}
|
|
247
273
|
}
|
|
248
274
|
}
|
|
249
275
|
}
|
|
250
276
|
if (CLASS_CONTAINER_TYPES.has(current.type)) {
|
|
277
|
+
// Delegate language-specific container remapping to the provider hook.
|
|
278
|
+
if (resolveEnclosingOwner) {
|
|
279
|
+
if (visitedContainers.has(current)) {
|
|
280
|
+
// We've already asked the hook about this container once — a loop
|
|
281
|
+
// would form (e.g., hook redirects to a child node whose parent is
|
|
282
|
+
// this same container). Skip and walk up.
|
|
283
|
+
current = current.parent;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
visitedContainers.add(current);
|
|
287
|
+
const resolved = resolveEnclosingOwner(current);
|
|
288
|
+
if (resolved === null) {
|
|
289
|
+
// Provider says skip this container — keep walking up.
|
|
290
|
+
current = current.parent;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (resolved !== current) {
|
|
294
|
+
// Provider remapped to a different node — re-evaluate from there.
|
|
295
|
+
current = resolved;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
251
299
|
// Rust impl_item: for `impl Trait for Struct {}`, pick the type after `for`
|
|
300
|
+
// NOTE: This impl_item ownership logic is duplicated in rust.ts:extractOwnerName.
|
|
301
|
+
// If modifying this block, update the other location too.
|
|
252
302
|
if (current.type === 'impl_item') {
|
|
253
303
|
const children = current.children ?? [];
|
|
254
304
|
const forIdx = children.findIndex((c) => c.text === 'for');
|
|
255
305
|
if (forIdx !== -1) {
|
|
256
306
|
const nameNode = children
|
|
257
307
|
.slice(forIdx + 1)
|
|
258
|
-
.find((c) => c.type === 'type_identifier' ||
|
|
308
|
+
.find((c) => c.type === 'type_identifier' ||
|
|
309
|
+
c.type === 'scoped_type_identifier' ||
|
|
310
|
+
c.type === 'identifier');
|
|
259
311
|
if (nameNode) {
|
|
260
|
-
return
|
|
312
|
+
return {
|
|
313
|
+
classId: generateId('Struct', `${filePath}:${nameNode.text}`),
|
|
314
|
+
className: nameNode.text,
|
|
315
|
+
};
|
|
261
316
|
}
|
|
262
317
|
}
|
|
263
|
-
|
|
318
|
+
const firstType = children.find((c) => c.type === 'type_identifier');
|
|
319
|
+
if (firstType) {
|
|
320
|
+
return {
|
|
321
|
+
classId: generateId('Impl', `${filePath}:${firstType.text}`),
|
|
322
|
+
className: firstType.text,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
264
325
|
}
|
|
265
326
|
const nameNode = current.childForFieldName?.('name') ??
|
|
266
327
|
current.children?.find((c) => c.type === 'type_identifier' ||
|
|
@@ -268,14 +329,29 @@ export const findEnclosingClassId = (node, filePath) => {
|
|
|
268
329
|
c.type === 'name' ||
|
|
269
330
|
c.type === 'constant');
|
|
270
331
|
if (nameNode) {
|
|
271
|
-
|
|
272
|
-
|
|
332
|
+
let label = CONTAINER_TYPE_TO_LABEL[current.type] || 'Class';
|
|
333
|
+
// Kotlin: class_declaration with an anonymous "interface" keyword child
|
|
334
|
+
// is actually an interface, not a class. Refine the label to match the
|
|
335
|
+
// node ID generated from the tree-sitter query capture (@definition.interface).
|
|
336
|
+
if (current.type === 'class_declaration' &&
|
|
337
|
+
label === 'Class' &&
|
|
338
|
+
current.children?.some((c) => c.type === 'interface')) {
|
|
339
|
+
label = 'Interface';
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
classId: generateId(label, `${filePath}:${nameNode.text}`),
|
|
343
|
+
className: nameNode.text,
|
|
344
|
+
};
|
|
273
345
|
}
|
|
274
346
|
}
|
|
275
347
|
current = current.parent;
|
|
276
348
|
}
|
|
277
349
|
return null;
|
|
278
350
|
};
|
|
351
|
+
/** Convenience wrapper: returns just the class ID string (backward compat). */
|
|
352
|
+
export const findEnclosingClassId = (node, filePath) => {
|
|
353
|
+
return findEnclosingClassInfo(node, filePath)?.classId ?? null;
|
|
354
|
+
};
|
|
279
355
|
/**
|
|
280
356
|
* Find a child of `childType` within a sibling node of `siblingType`.
|
|
281
357
|
* Used for Kotlin AST traversal where visibility_modifier lives inside a modifiers sibling.
|
|
@@ -293,537 +369,57 @@ export const findSiblingChild = (parent, siblingType, childType) => {
|
|
|
293
369
|
}
|
|
294
370
|
return null;
|
|
295
371
|
};
|
|
296
|
-
/**
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
if (FUNCTION_DECLARATION_TYPES.has(node.type)) {
|
|
311
|
-
// C/C++: function_definition -> [pointer_declarator ->] function_declarator -> qualified_identifier/identifier
|
|
312
|
-
// Unwrap pointer_declarator / reference_declarator wrappers to reach function_declarator
|
|
313
|
-
let declarator = node.childForFieldName?.('declarator');
|
|
314
|
-
if (!declarator) {
|
|
315
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
316
|
-
const c = node.child(i);
|
|
317
|
-
if (c?.type === 'function_declarator') {
|
|
318
|
-
declarator = c;
|
|
319
|
-
break;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
while (declarator &&
|
|
324
|
-
(declarator.type === 'pointer_declarator' || declarator.type === 'reference_declarator')) {
|
|
325
|
-
let nextDeclarator = declarator.childForFieldName?.('declarator');
|
|
326
|
-
if (!nextDeclarator) {
|
|
327
|
-
for (let i = 0; i < declarator.childCount; i++) {
|
|
328
|
-
const c = declarator.child(i);
|
|
329
|
-
if (c?.type === 'function_declarator' ||
|
|
330
|
-
c?.type === 'pointer_declarator' ||
|
|
331
|
-
c?.type === 'reference_declarator') {
|
|
332
|
-
nextDeclarator = c;
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
declarator = nextDeclarator;
|
|
338
|
-
}
|
|
339
|
-
if (declarator) {
|
|
340
|
-
let innerDeclarator = declarator.childForFieldName?.('declarator');
|
|
341
|
-
if (!innerDeclarator) {
|
|
342
|
-
for (let i = 0; i < declarator.childCount; i++) {
|
|
343
|
-
const c = declarator.child(i);
|
|
344
|
-
if (c?.type === 'qualified_identifier' ||
|
|
345
|
-
c?.type === 'identifier' ||
|
|
346
|
-
c?.type === 'field_identifier' ||
|
|
347
|
-
c?.type === 'parenthesized_declarator') {
|
|
348
|
-
innerDeclarator = c;
|
|
349
|
-
break;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
if (innerDeclarator?.type === 'qualified_identifier') {
|
|
354
|
-
let nameNode = innerDeclarator.childForFieldName?.('name');
|
|
355
|
-
if (!nameNode) {
|
|
356
|
-
for (let i = 0; i < innerDeclarator.childCount; i++) {
|
|
357
|
-
const c = innerDeclarator.child(i);
|
|
358
|
-
if (c?.type === 'identifier') {
|
|
359
|
-
nameNode = c;
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
if (nameNode?.text) {
|
|
365
|
-
funcName = nameNode.text;
|
|
366
|
-
label = 'Method';
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
else if (innerDeclarator?.type === 'identifier' ||
|
|
370
|
-
innerDeclarator?.type === 'field_identifier') {
|
|
371
|
-
// field_identifier is used for method names inside C++ class bodies
|
|
372
|
-
funcName = innerDeclarator.text;
|
|
373
|
-
if (innerDeclarator.type === 'field_identifier')
|
|
374
|
-
label = 'Method';
|
|
375
|
-
}
|
|
376
|
-
else if (innerDeclarator?.type === 'parenthesized_declarator') {
|
|
377
|
-
let nestedId = null;
|
|
378
|
-
for (let i = 0; i < innerDeclarator.childCount; i++) {
|
|
379
|
-
const c = innerDeclarator.child(i);
|
|
380
|
-
if (c?.type === 'qualified_identifier' || c?.type === 'identifier') {
|
|
381
|
-
nestedId = c;
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
if (nestedId?.type === 'qualified_identifier') {
|
|
386
|
-
let nameNode = nestedId.childForFieldName?.('name');
|
|
387
|
-
if (!nameNode) {
|
|
388
|
-
for (let i = 0; i < nestedId.childCount; i++) {
|
|
389
|
-
const c = nestedId.child(i);
|
|
390
|
-
if (c?.type === 'identifier') {
|
|
391
|
-
nameNode = c;
|
|
392
|
-
break;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
if (nameNode?.text) {
|
|
397
|
-
funcName = nameNode.text;
|
|
398
|
-
label = 'Method';
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
else if (nestedId?.type === 'identifier') {
|
|
402
|
-
funcName = nestedId.text;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
// Fallback for other languages (Kotlin uses simple_identifier, Swift uses simple_identifier)
|
|
407
|
-
if (!funcName) {
|
|
408
|
-
let nameNode = node.childForFieldName?.('name');
|
|
409
|
-
if (!nameNode) {
|
|
410
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
411
|
-
const c = node.child(i);
|
|
412
|
-
if (c?.type === 'identifier' ||
|
|
413
|
-
c?.type === 'property_identifier' ||
|
|
414
|
-
c?.type === 'simple_identifier') {
|
|
415
|
-
nameNode = c;
|
|
416
|
-
break;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
funcName = nameNode?.text;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
else if (node.type === 'impl_item') {
|
|
424
|
-
let funcItem = null;
|
|
425
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
426
|
-
const c = node.child(i);
|
|
427
|
-
if (c?.type === 'function_item') {
|
|
428
|
-
funcItem = c;
|
|
429
|
-
break;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
if (funcItem) {
|
|
433
|
-
let nameNode = funcItem.childForFieldName?.('name');
|
|
434
|
-
if (!nameNode) {
|
|
435
|
-
for (let i = 0; i < funcItem.childCount; i++) {
|
|
436
|
-
const c = funcItem.child(i);
|
|
437
|
-
if (c?.type === 'identifier') {
|
|
438
|
-
nameNode = c;
|
|
439
|
-
break;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
funcName = nameNode?.text;
|
|
444
|
-
label = 'Method';
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
else if (node.type === 'method_definition') {
|
|
448
|
-
let nameNode = node.childForFieldName?.('name');
|
|
449
|
-
if (!nameNode) {
|
|
450
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
451
|
-
const c = node.child(i);
|
|
452
|
-
if (c?.type === 'property_identifier') {
|
|
453
|
-
nameNode = c;
|
|
454
|
-
break;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
funcName = nameNode?.text;
|
|
459
|
-
label = 'Method';
|
|
460
|
-
}
|
|
461
|
-
else if (node.type === 'method_declaration' || node.type === 'constructor_declaration') {
|
|
462
|
-
let nameNode = node.childForFieldName?.('name');
|
|
463
|
-
if (!nameNode) {
|
|
464
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
465
|
-
const c = node.child(i);
|
|
466
|
-
if (c?.type === 'identifier') {
|
|
467
|
-
nameNode = c;
|
|
468
|
-
break;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
funcName = nameNode?.text;
|
|
473
|
-
label = 'Method';
|
|
474
|
-
}
|
|
475
|
-
else if (node.type === 'arrow_function' || node.type === 'function_expression') {
|
|
476
|
-
const parent = node.parent;
|
|
477
|
-
if (parent?.type === 'variable_declarator') {
|
|
478
|
-
let nameNode = parent.childForFieldName?.('name');
|
|
479
|
-
if (!nameNode) {
|
|
480
|
-
for (let i = 0; i < parent.childCount; i++) {
|
|
481
|
-
const c = parent.child(i);
|
|
482
|
-
if (c?.type === 'identifier') {
|
|
483
|
-
nameNode = c;
|
|
484
|
-
break;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
funcName = nameNode?.text;
|
|
489
|
-
}
|
|
372
|
+
/** Generic name extraction from a function-like AST node.
|
|
373
|
+
* Tries `node.childForFieldName('name')?.text`, then scans children for
|
|
374
|
+
* `identifier` / `property_identifier` / `simple_identifier`. */
|
|
375
|
+
export const genericFuncName = (node) => {
|
|
376
|
+
const nameField = node.childForFieldName?.('name');
|
|
377
|
+
if (nameField)
|
|
378
|
+
return nameField.text;
|
|
379
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
380
|
+
const c = node.child(i);
|
|
381
|
+
if (c?.type === 'identifier' ||
|
|
382
|
+
c?.type === 'property_identifier' ||
|
|
383
|
+
c?.type === 'simple_identifier')
|
|
384
|
+
return c.text;
|
|
490
385
|
}
|
|
491
|
-
|
|
492
|
-
let nameNode = node.childForFieldName?.('name');
|
|
493
|
-
if (!nameNode) {
|
|
494
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
495
|
-
const c = node.child(i);
|
|
496
|
-
if (c?.type === 'identifier') {
|
|
497
|
-
nameNode = c;
|
|
498
|
-
break;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
funcName = nameNode?.text;
|
|
503
|
-
label = 'Method';
|
|
504
|
-
}
|
|
505
|
-
else if (node.type === 'function_signature') {
|
|
506
|
-
// Dart: top-level function signatures
|
|
507
|
-
let nameNode = node.childForFieldName?.('name');
|
|
508
|
-
if (!nameNode) {
|
|
509
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
510
|
-
const c = node.child(i);
|
|
511
|
-
if (c?.type === 'identifier') {
|
|
512
|
-
nameNode = c;
|
|
513
|
-
break;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
funcName = nameNode?.text ?? null;
|
|
518
|
-
}
|
|
519
|
-
else if (node.type === 'method_signature') {
|
|
520
|
-
// Dart: method_signature wraps function_signature
|
|
521
|
-
let funcSig = null;
|
|
522
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
523
|
-
const c = node.child(i);
|
|
524
|
-
if (c?.type === 'function_signature') {
|
|
525
|
-
funcSig = c;
|
|
526
|
-
break;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
if (funcSig) {
|
|
530
|
-
let nameNode = funcSig.childForFieldName?.('name');
|
|
531
|
-
if (!nameNode) {
|
|
532
|
-
for (let i = 0; i < funcSig.childCount; i++) {
|
|
533
|
-
const c = funcSig.child(i);
|
|
534
|
-
if (c?.type === 'identifier') {
|
|
535
|
-
nameNode = c;
|
|
536
|
-
break;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
funcName = nameNode?.text ?? null;
|
|
541
|
-
}
|
|
542
|
-
label = 'Method';
|
|
543
|
-
}
|
|
544
|
-
return { funcName, label };
|
|
386
|
+
return null;
|
|
545
387
|
};
|
|
546
|
-
/**
|
|
388
|
+
/** AST node types that represent a method definition (for `inferFunctionLabel`). */
|
|
389
|
+
export const METHOD_LABEL_NODE_TYPES = new Set([
|
|
390
|
+
'method_definition',
|
|
391
|
+
'method_declaration',
|
|
392
|
+
'method',
|
|
393
|
+
'singleton_method',
|
|
394
|
+
]);
|
|
395
|
+
/** AST node types that represent a constructor definition (for `inferFunctionLabel`). */
|
|
396
|
+
export const CONSTRUCTOR_LABEL_NODE_TYPES = new Set([
|
|
397
|
+
'constructor_declaration',
|
|
398
|
+
'compact_constructor_declaration',
|
|
399
|
+
]);
|
|
400
|
+
/** Infer node label from AST node type for function-like nodes without a provider hook. */
|
|
401
|
+
export const inferFunctionLabel = (nodeType) => METHOD_LABEL_NODE_TYPES.has(nodeType)
|
|
402
|
+
? 'Method'
|
|
403
|
+
: CONSTRUCTOR_LABEL_NODE_TYPES.has(nodeType)
|
|
404
|
+
? 'Constructor'
|
|
405
|
+
: 'Function';
|
|
406
|
+
/** Argument list node types shared between countCallArguments and call-resolution helpers. */
|
|
547
407
|
export const CALL_ARGUMENT_LIST_TYPES = new Set(['arguments', 'argument_list', 'value_arguments']);
|
|
548
|
-
/**
|
|
549
|
-
* Extract parameter count and return type text from an AST method/function node.
|
|
550
|
-
* Works across languages by looking for common AST patterns.
|
|
551
|
-
*/
|
|
552
|
-
export const extractMethodSignature = (node) => {
|
|
553
|
-
let parameterCount = 0;
|
|
554
|
-
let requiredCount = 0;
|
|
555
|
-
let returnType;
|
|
556
|
-
let isVariadic = false;
|
|
557
|
-
const paramTypes = [];
|
|
558
|
-
if (!node)
|
|
559
|
-
return {
|
|
560
|
-
parameterCount,
|
|
561
|
-
requiredParameterCount: undefined,
|
|
562
|
-
parameterTypes: undefined,
|
|
563
|
-
returnType,
|
|
564
|
-
};
|
|
565
|
-
const paramListTypes = new Set([
|
|
566
|
-
'formal_parameters',
|
|
567
|
-
'parameters',
|
|
568
|
-
'parameter_list',
|
|
569
|
-
'function_parameters',
|
|
570
|
-
'method_parameters',
|
|
571
|
-
'function_value_parameters',
|
|
572
|
-
'formal_parameter_list', // Dart
|
|
573
|
-
]);
|
|
574
|
-
// Node types that indicate variadic/rest parameters
|
|
575
|
-
const VARIADIC_PARAM_TYPES = new Set([
|
|
576
|
-
'variadic_parameter_declaration', // Go: ...string
|
|
577
|
-
'variadic_parameter', // Rust: extern "C" fn(...)
|
|
578
|
-
'spread_parameter', // Java: Object... args
|
|
579
|
-
'list_splat_pattern', // Python: *args
|
|
580
|
-
'dictionary_splat_pattern', // Python: **kwargs
|
|
581
|
-
]);
|
|
582
|
-
/** AST node types that represent parameters with default values. */
|
|
583
|
-
const OPTIONAL_PARAM_TYPES = new Set([
|
|
584
|
-
'optional_parameter', // TypeScript, Ruby: (x?: number), (x: number = 5), def f(x = 5)
|
|
585
|
-
'default_parameter', // Python: def f(x=5)
|
|
586
|
-
'typed_default_parameter', // Python: def f(x: int = 5)
|
|
587
|
-
'optional_parameter_declaration', // C++: void f(int x = 5)
|
|
588
|
-
]);
|
|
589
|
-
/** Check if a parameter node has a default value (handles Kotlin, C#, Swift, PHP
|
|
590
|
-
* where defaults are expressed as child nodes rather than distinct node types). */
|
|
591
|
-
const hasDefaultValue = (paramNode) => {
|
|
592
|
-
if (OPTIONAL_PARAM_TYPES.has(paramNode.type))
|
|
593
|
-
return true;
|
|
594
|
-
// C#, Swift, PHP: check for '=' token or equals_value_clause child
|
|
595
|
-
for (let i = 0; i < paramNode.childCount; i++) {
|
|
596
|
-
const c = paramNode.child(i);
|
|
597
|
-
if (!c)
|
|
598
|
-
continue;
|
|
599
|
-
if (c.type === '=' || c.type === 'equals_value_clause')
|
|
600
|
-
return true;
|
|
601
|
-
}
|
|
602
|
-
// Kotlin: default values are siblings of the parameter node, not children.
|
|
603
|
-
// The AST is: parameter, =, <literal> — all at function_value_parameters level.
|
|
604
|
-
// Check if the immediately following sibling is '=' (default value separator).
|
|
605
|
-
const sib = paramNode.nextSibling;
|
|
606
|
-
if (sib && sib.type === '=')
|
|
607
|
-
return true;
|
|
608
|
-
return false;
|
|
609
|
-
};
|
|
610
|
-
const findParameterList = (current) => {
|
|
611
|
-
for (const child of current.children) {
|
|
612
|
-
if (paramListTypes.has(child.type))
|
|
613
|
-
return child;
|
|
614
|
-
}
|
|
615
|
-
for (const child of current.children) {
|
|
616
|
-
const nested = findParameterList(child);
|
|
617
|
-
if (nested)
|
|
618
|
-
return nested;
|
|
619
|
-
}
|
|
620
|
-
return null;
|
|
621
|
-
};
|
|
622
|
-
const parameterList = paramListTypes.has(node.type)
|
|
623
|
-
? node // node itself IS the parameter list (e.g. C# primary constructors)
|
|
624
|
-
: (node.childForFieldName?.('parameters') ?? findParameterList(node));
|
|
625
|
-
if (parameterList && paramListTypes.has(parameterList.type)) {
|
|
626
|
-
for (const param of parameterList.namedChildren) {
|
|
627
|
-
if (param.type === 'comment')
|
|
628
|
-
continue;
|
|
629
|
-
if (param.text === 'self' ||
|
|
630
|
-
param.text === '&self' ||
|
|
631
|
-
param.text === '&mut self' ||
|
|
632
|
-
param.type === 'self_parameter') {
|
|
633
|
-
continue;
|
|
634
|
-
}
|
|
635
|
-
// TypeScript: `this` parameter is a compile-time type constraint, not a real param
|
|
636
|
-
// e.g., handle(this: void, event: Event) — only count 'event'
|
|
637
|
-
if (param.type === 'required_parameter') {
|
|
638
|
-
const patternNode = param.childForFieldName('pattern');
|
|
639
|
-
if (patternNode?.type === 'this')
|
|
640
|
-
continue;
|
|
641
|
-
}
|
|
642
|
-
// Kotlin: default values are siblings of the parameter node inside
|
|
643
|
-
// function_value_parameters, so they appear as named children (e.g.
|
|
644
|
-
// string_literal, integer_literal, boolean_literal, call_expression).
|
|
645
|
-
// Skip any named child that isn't a parameter-like or modifier node.
|
|
646
|
-
if (param.type.endsWith('_literal') ||
|
|
647
|
-
param.type === 'call_expression' ||
|
|
648
|
-
param.type === 'navigation_expression' ||
|
|
649
|
-
param.type === 'prefix_expression' ||
|
|
650
|
-
param.type === 'parenthesized_expression') {
|
|
651
|
-
continue;
|
|
652
|
-
}
|
|
653
|
-
// Check for variadic parameter types
|
|
654
|
-
if (VARIADIC_PARAM_TYPES.has(param.type)) {
|
|
655
|
-
isVariadic = true;
|
|
656
|
-
continue;
|
|
657
|
-
}
|
|
658
|
-
// TypeScript/JavaScript: rest parameter — required_parameter containing rest_pattern
|
|
659
|
-
if (param.type === 'required_parameter' || param.type === 'optional_parameter') {
|
|
660
|
-
for (const child of param.children) {
|
|
661
|
-
if (child.type === 'rest_pattern') {
|
|
662
|
-
isVariadic = true;
|
|
663
|
-
break;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
if (isVariadic)
|
|
667
|
-
continue;
|
|
668
|
-
}
|
|
669
|
-
// Kotlin: vararg modifier on a regular parameter
|
|
670
|
-
if (param.type === 'parameter' || param.type === 'formal_parameter') {
|
|
671
|
-
const prev = param.previousSibling;
|
|
672
|
-
if (prev?.type === 'parameter_modifiers' && prev.text.includes('vararg')) {
|
|
673
|
-
isVariadic = true;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
// Extract parameter type name for overload disambiguation.
|
|
677
|
-
// Works for Java (formal_parameter), Kotlin (parameter), C# (parameter),
|
|
678
|
-
// C++ (parameter_declaration). Uses childForFieldName('type') which is the
|
|
679
|
-
// standard tree-sitter field for typed parameters across these languages.
|
|
680
|
-
// Kotlin uses positional children instead of 'type' field — fall back to
|
|
681
|
-
// searching for user_type/nullable_type/predefined_type children.
|
|
682
|
-
const paramTypeNode = param.childForFieldName('type');
|
|
683
|
-
if (paramTypeNode) {
|
|
684
|
-
const typeName = extractSimpleTypeName(paramTypeNode);
|
|
685
|
-
paramTypes.push(typeName ?? 'unknown');
|
|
686
|
-
}
|
|
687
|
-
else {
|
|
688
|
-
// Kotlin: parameter → [simple_identifier, user_type|nullable_type]
|
|
689
|
-
let found = false;
|
|
690
|
-
for (const child of param.namedChildren) {
|
|
691
|
-
if (child.type === 'user_type' ||
|
|
692
|
-
child.type === 'nullable_type' ||
|
|
693
|
-
child.type === 'type_identifier' ||
|
|
694
|
-
child.type === 'predefined_type') {
|
|
695
|
-
const typeName = extractSimpleTypeName(child);
|
|
696
|
-
paramTypes.push(typeName ?? 'unknown');
|
|
697
|
-
found = true;
|
|
698
|
-
break;
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
if (!found)
|
|
702
|
-
paramTypes.push('unknown');
|
|
703
|
-
}
|
|
704
|
-
if (!hasDefaultValue(param))
|
|
705
|
-
requiredCount++;
|
|
706
|
-
parameterCount++;
|
|
707
|
-
}
|
|
708
|
-
// C/C++: bare `...` token in parameter list (not a named child — check all children)
|
|
709
|
-
if (!isVariadic) {
|
|
710
|
-
for (const child of parameterList.children) {
|
|
711
|
-
if (!child.isNamed && child.text === '...') {
|
|
712
|
-
isVariadic = true;
|
|
713
|
-
break;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
// Swift fallback: tree-sitter-swift places `parameter` nodes as direct children of
|
|
719
|
-
// function_declaration without a wrapping parameters/function_parameters list node.
|
|
720
|
-
// When no parameter list was found, count direct `parameter` children on the node.
|
|
721
|
-
if (!parameterList && parameterCount === 0) {
|
|
722
|
-
for (const child of node.namedChildren) {
|
|
723
|
-
if (child.type === 'parameter') {
|
|
724
|
-
if (!hasDefaultValue(child))
|
|
725
|
-
requiredCount++;
|
|
726
|
-
parameterCount++;
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
// Return type extraction — language-specific field names
|
|
731
|
-
// Go: 'result' field is either a type_identifier or parameter_list (multi-return)
|
|
732
|
-
const goResult = node.childForFieldName?.('result');
|
|
733
|
-
if (goResult) {
|
|
734
|
-
if (goResult.type === 'parameter_list') {
|
|
735
|
-
// Multi-return: extract first parameter's type only (e.g. (*User, error) → *User)
|
|
736
|
-
const firstParam = goResult.firstNamedChild;
|
|
737
|
-
if (firstParam?.type === 'parameter_declaration') {
|
|
738
|
-
const typeNode = firstParam.childForFieldName('type');
|
|
739
|
-
if (typeNode)
|
|
740
|
-
returnType = typeNode.text;
|
|
741
|
-
}
|
|
742
|
-
else if (firstParam) {
|
|
743
|
-
// Unnamed return types: (string, error) — first child is a bare type node
|
|
744
|
-
returnType = firstParam.text;
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
else {
|
|
748
|
-
returnType = goResult.text;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
// Rust: 'return_type' field — the value IS the type node (e.g. primitive_type, type_identifier).
|
|
752
|
-
// Skip if the node is a type_annotation (TS/Python), which is handled by the generic loop below.
|
|
753
|
-
if (!returnType) {
|
|
754
|
-
const rustReturn = node.childForFieldName?.('return_type');
|
|
755
|
-
if (rustReturn && rustReturn.type !== 'type_annotation') {
|
|
756
|
-
returnType = rustReturn.text;
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
// C/C++: 'type' field on function_definition
|
|
760
|
-
if (!returnType) {
|
|
761
|
-
const cppType = node.childForFieldName?.('type');
|
|
762
|
-
if (cppType && cppType.text !== 'void') {
|
|
763
|
-
returnType = cppType.text;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
// C#: 'returns' field on method_declaration
|
|
767
|
-
if (!returnType) {
|
|
768
|
-
const csReturn = node.childForFieldName?.('returns');
|
|
769
|
-
if (csReturn && csReturn.text !== 'void') {
|
|
770
|
-
returnType = csReturn.text;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
// TS/Rust/Python/C#/Kotlin: type_annotation or return_type child
|
|
774
|
-
if (!returnType) {
|
|
775
|
-
for (const child of node.children) {
|
|
776
|
-
if (child.type === 'type_annotation' || child.type === 'return_type') {
|
|
777
|
-
const typeNode = child.children.find((c) => c.isNamed);
|
|
778
|
-
if (typeNode)
|
|
779
|
-
returnType = typeNode.text;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
// Kotlin: fun getUser(): User — return type is a bare user_type child of
|
|
784
|
-
// function_declaration. The Kotlin grammar does NOT wrap it in type_annotation
|
|
785
|
-
// or return_type; it appears as a direct child after function_value_parameters.
|
|
786
|
-
// Note: Kotlin uses function_value_parameters (not a field), so we find it by type.
|
|
787
|
-
if (!returnType) {
|
|
788
|
-
let paramsEnd = -1;
|
|
789
|
-
for (let i = 0; i < node.childCount; i++) {
|
|
790
|
-
const child = node.child(i);
|
|
791
|
-
if (!child)
|
|
792
|
-
continue;
|
|
793
|
-
if (child.type === 'function_value_parameters' || child.type === 'value_parameters') {
|
|
794
|
-
paramsEnd = child.endIndex;
|
|
795
|
-
}
|
|
796
|
-
if (paramsEnd >= 0 && child.type === 'user_type' && child.startIndex > paramsEnd) {
|
|
797
|
-
returnType = child.text;
|
|
798
|
-
break;
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
if (isVariadic)
|
|
803
|
-
parameterCount = undefined;
|
|
804
|
-
// Only include parameterTypes when at least one type was successfully extracted.
|
|
805
|
-
// Use undefined (not []) to avoid empty array allocations for untyped parameters.
|
|
806
|
-
const hasTypes = paramTypes.length > 0 && paramTypes.some((t) => t !== 'unknown');
|
|
807
|
-
// Only set requiredParameterCount when it differs from total — saves memory on the common case.
|
|
808
|
-
const requiredParameterCount = !isVariadic && requiredCount < (parameterCount ?? 0) ? requiredCount : undefined;
|
|
809
|
-
return {
|
|
810
|
-
parameterCount,
|
|
811
|
-
requiredParameterCount,
|
|
812
|
-
parameterTypes: hasTypes ? paramTypes : undefined,
|
|
813
|
-
returnType,
|
|
814
|
-
};
|
|
815
|
-
};
|
|
816
408
|
// ============================================================================
|
|
817
409
|
// Generic AST traversal helpers (shared by parse-worker + php-helpers)
|
|
818
410
|
// ============================================================================
|
|
819
411
|
/** Walk an AST node depth-first, returning the first descendant with the given type. */
|
|
820
|
-
export function findDescendant(
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
412
|
+
export function findDescendant(root, type) {
|
|
413
|
+
const stack = [root];
|
|
414
|
+
while (stack.length > 0) {
|
|
415
|
+
const node = stack.pop();
|
|
416
|
+
if (node.type === type)
|
|
417
|
+
return node;
|
|
418
|
+
// Push in reverse order so left children are visited first (depth-first)
|
|
419
|
+
const children = node.children ?? [];
|
|
420
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
421
|
+
stack.push(children[i]);
|
|
422
|
+
}
|
|
827
423
|
}
|
|
828
424
|
return null;
|
|
829
425
|
}
|
|
@@ -838,18 +434,6 @@ export function extractStringContent(node) {
|
|
|
838
434
|
return node.text;
|
|
839
435
|
return null;
|
|
840
436
|
}
|
|
841
|
-
/** Check if a C/C++ function_definition is inside a class or struct body.
|
|
842
|
-
* Used by the C/C++ labelOverride to skip duplicate function captures
|
|
843
|
-
* that are already covered by definition.method queries. */
|
|
844
|
-
export function isCppInsideClassOrStruct(functionNode) {
|
|
845
|
-
let ancestor = functionNode?.parent ?? null;
|
|
846
|
-
while (ancestor) {
|
|
847
|
-
if (ancestor.type === 'class_specifier' || ancestor.type === 'struct_specifier')
|
|
848
|
-
return true;
|
|
849
|
-
ancestor = ancestor.parent;
|
|
850
|
-
}
|
|
851
|
-
return false;
|
|
852
|
-
}
|
|
853
437
|
/** Find the first direct named child of a tree-sitter node matching the given type. */
|
|
854
438
|
export function findChild(node, type) {
|
|
855
439
|
for (let i = 0; i < node.namedChildCount; i++) {
|