gitnexus 1.5.3 → 1.6.0
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 +2 -1
- 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/grpc-extractor.d.ts +16 -0
- package/dist/core/group/extractors/grpc-extractor.js +264 -0
- package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
- package/dist/core/group/extractors/http-route-extractor.js +428 -0
- package/dist/core/group/extractors/topic-extractor.d.ts +9 -0
- package/dist/core/group/extractors/topic-extractor.js +234 -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 +207 -0
- package/dist/core/ingestion/binding-accumulator.js +332 -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/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 +0 -12
- 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-provider.d.ts +6 -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 +28 -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/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 +13 -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 +285 -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.js +38 -15
- package/dist/core/ingestion/method-types.d.ts +25 -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 +242 -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 +4 -3
- package/dist/core/ingestion/mro-processor.js +310 -106
- package/dist/core/ingestion/parsing-processor.d.ts +5 -4
- package/dist/core/ingestion/parsing-processor.js +210 -85
- package/dist/core/ingestion/pipeline.d.ts +2 -0
- package/dist/core/ingestion/pipeline.js +192 -68
- package/dist/core/ingestion/tree-sitter-queries.d.ts +5 -5
- package/dist/core/ingestion/tree-sitter-queries.js +21 -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 +33 -43
- package/dist/core/ingestion/utils/ast-helpers.js +129 -572
- 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 +463 -198
- package/dist/core/lbug/lbug-adapter.d.ts +6 -0
- package/dist/core/lbug/lbug-adapter.js +68 -3
- 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 +8 -2
- package/scripts/patch-tree-sitter-swift.cjs +78 -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
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// gitnexus/src/core/ingestion/method-extractors/configs/php.ts
|
|
2
|
+
// Verified against tree-sitter-php 0.23.12
|
|
3
|
+
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
4
|
+
import { extractSimpleTypeName } from '../../type-extractors/shared.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// PHP helpers
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
/** Regex to extract PHPDoc @return annotations: `@return User` */
|
|
9
|
+
const PHPDOC_RETURN_RE = /@return\s+(\S+)/;
|
|
10
|
+
/** Node types to skip when walking backwards through siblings for PHPDoc. */
|
|
11
|
+
const PHPDOC_SKIP_NODE_TYPES = new Set(['attribute_list', 'attribute']);
|
|
12
|
+
/**
|
|
13
|
+
* Normalize a PHPDoc return type for the MethodExtractor.
|
|
14
|
+
* Strips nullable prefix, null/false/void unions, namespace prefixes, and
|
|
15
|
+
* rejects uninformative types (mixed, void, self, static, object, array).
|
|
16
|
+
*/
|
|
17
|
+
function normalizePhpReturnType(raw) {
|
|
18
|
+
let type = raw.startsWith('?') ? raw.slice(1) : raw;
|
|
19
|
+
const parts = type
|
|
20
|
+
.split('|')
|
|
21
|
+
.filter((p) => p !== 'null' && p !== 'false' && p !== 'void' && p !== 'mixed');
|
|
22
|
+
if (parts.length !== 1)
|
|
23
|
+
return undefined;
|
|
24
|
+
type = parts[0];
|
|
25
|
+
const segments = type.split('\\');
|
|
26
|
+
type = segments[segments.length - 1];
|
|
27
|
+
if (type === 'mixed' ||
|
|
28
|
+
type === 'void' ||
|
|
29
|
+
type === 'self' ||
|
|
30
|
+
type === 'static' ||
|
|
31
|
+
type === 'object' ||
|
|
32
|
+
type === 'array')
|
|
33
|
+
return undefined;
|
|
34
|
+
if (/^\w+(\[\])?$/.test(type) || /^\w+\s*</.test(type))
|
|
35
|
+
return type;
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Walk backwards through preceding siblings of `node` to find a PHPDoc
|
|
40
|
+
* `@return Type` annotation. Skips `attribute_list` nodes (PHP 8 attributes).
|
|
41
|
+
*/
|
|
42
|
+
function extractPhpDocReturnType(node) {
|
|
43
|
+
let sibling = node.previousSibling;
|
|
44
|
+
while (sibling) {
|
|
45
|
+
if (sibling.type === 'comment') {
|
|
46
|
+
const match = PHPDOC_RETURN_RE.exec(sibling.text);
|
|
47
|
+
if (match)
|
|
48
|
+
return normalizePhpReturnType(match[1]);
|
|
49
|
+
}
|
|
50
|
+
else if (sibling.isNamed && !PHPDOC_SKIP_NODE_TYPES.has(sibling.type)) {
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
sibling = sibling.previousSibling;
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const PHP_VIS = new Set(['public', 'private', 'protected']);
|
|
58
|
+
/**
|
|
59
|
+
* Find the visibility keyword from a visibility_modifier named child.
|
|
60
|
+
* PHP tree-sitter emits `visibility_modifier` as a named node with text
|
|
61
|
+
* "public", "private", or "protected".
|
|
62
|
+
*/
|
|
63
|
+
function findPhpVisibility(node) {
|
|
64
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
65
|
+
const child = node.namedChild(i);
|
|
66
|
+
if (child?.type === 'visibility_modifier') {
|
|
67
|
+
const text = child.text.trim();
|
|
68
|
+
if (PHP_VIS.has(text))
|
|
69
|
+
return text;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return 'public'; // PHP methods are public by default
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Check for a named modifier child of a specific type.
|
|
76
|
+
* PHP tree-sitter uses distinct node types: abstract_modifier, final_modifier,
|
|
77
|
+
* static_modifier — rather than a wrapper `modifiers` node with keyword children.
|
|
78
|
+
*/
|
|
79
|
+
function hasModifierNode(node, modifierType) {
|
|
80
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
81
|
+
const child = node.namedChild(i);
|
|
82
|
+
if (child?.type === modifierType)
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Extract the return type from a PHP method_declaration node.
|
|
89
|
+
*
|
|
90
|
+
* In tree-sitter-php, the return type is not exposed via a named field.
|
|
91
|
+
* It appears as a type node (primitive_type, named_type, union_type,
|
|
92
|
+
* optional_type, nullable_type, intersection_type) after the formal_parameters
|
|
93
|
+
* and a `:` token separator.
|
|
94
|
+
*
|
|
95
|
+
* When the AST return type is missing or uninformative (`array` / `iterable`),
|
|
96
|
+
* falls back to parsing PHPDoc `@return Type` from preceding doc comments.
|
|
97
|
+
*/
|
|
98
|
+
function extractPhpReturnType(node) {
|
|
99
|
+
const TYPE_NODE_TYPES = new Set([
|
|
100
|
+
'primitive_type',
|
|
101
|
+
'named_type',
|
|
102
|
+
'union_type',
|
|
103
|
+
'optional_type',
|
|
104
|
+
'nullable_type',
|
|
105
|
+
'intersection_type',
|
|
106
|
+
]);
|
|
107
|
+
let astType;
|
|
108
|
+
let seenParams = false;
|
|
109
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
110
|
+
const child = node.child(i);
|
|
111
|
+
if (!child)
|
|
112
|
+
continue;
|
|
113
|
+
if (child.type === 'formal_parameters') {
|
|
114
|
+
seenParams = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
// After the parameters node, look for the colon and then the type
|
|
118
|
+
if (seenParams && child.isNamed && TYPE_NODE_TYPES.has(child.type)) {
|
|
119
|
+
astType = child.text?.trim();
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
// Stop at body or semicolon
|
|
123
|
+
if (child.type === 'compound_statement' || (!child.isNamed && child.text === ';')) {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// If AST type is missing or uninformative, try PHPDoc @return fallback
|
|
128
|
+
if (!astType || astType === 'array' || astType === 'iterable') {
|
|
129
|
+
const docType = extractPhpDocReturnType(node);
|
|
130
|
+
if (docType)
|
|
131
|
+
return docType;
|
|
132
|
+
}
|
|
133
|
+
return astType;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Extract parameters from a PHP method_declaration node.
|
|
137
|
+
*
|
|
138
|
+
* PHP parameter types in tree-sitter-php:
|
|
139
|
+
* - `simple_parameter`: regular parameter with optional type and default
|
|
140
|
+
* - `variadic_parameter`: `...$param` with optional type
|
|
141
|
+
* - `property_promotion_parameter`: constructor promotion `private string $name`
|
|
142
|
+
* (may also be variadic via an ERROR node containing `...`)
|
|
143
|
+
*/
|
|
144
|
+
function extractPhpParameters(node) {
|
|
145
|
+
const paramList = node.childForFieldName('parameters');
|
|
146
|
+
if (!paramList)
|
|
147
|
+
return [];
|
|
148
|
+
const params = [];
|
|
149
|
+
for (let i = 0; i < paramList.namedChildCount; i++) {
|
|
150
|
+
const param = paramList.namedChild(i);
|
|
151
|
+
if (!param)
|
|
152
|
+
continue;
|
|
153
|
+
if (param.type === 'simple_parameter') {
|
|
154
|
+
const nameNode = param.childForFieldName('name');
|
|
155
|
+
if (!nameNode)
|
|
156
|
+
continue;
|
|
157
|
+
const typeNode = param.childForFieldName('type');
|
|
158
|
+
const typeName = typeNode ? (extractSimpleTypeName(typeNode) ?? typeNode.text?.trim()) : null;
|
|
159
|
+
// Detect optional: '=' token among children indicates a default value
|
|
160
|
+
let isOptional = false;
|
|
161
|
+
for (let j = 0; j < param.childCount; j++) {
|
|
162
|
+
const c = param.child(j);
|
|
163
|
+
if (c && !c.isNamed && c.text === '=') {
|
|
164
|
+
isOptional = true;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
params.push({
|
|
169
|
+
name: stripDollar(nameNode.text),
|
|
170
|
+
type: typeName ?? null,
|
|
171
|
+
rawType: typeNode?.text?.trim() ?? null,
|
|
172
|
+
isOptional,
|
|
173
|
+
isVariadic: false,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
else if (param.type === 'variadic_parameter') {
|
|
177
|
+
const nameNode = param.childForFieldName('name');
|
|
178
|
+
if (!nameNode)
|
|
179
|
+
continue;
|
|
180
|
+
const typeNode = param.childForFieldName('type');
|
|
181
|
+
const typeName = typeNode ? (extractSimpleTypeName(typeNode) ?? typeNode.text?.trim()) : null;
|
|
182
|
+
params.push({
|
|
183
|
+
name: stripDollar(nameNode.text),
|
|
184
|
+
type: typeName ?? null,
|
|
185
|
+
rawType: typeNode?.text?.trim() ?? null,
|
|
186
|
+
isOptional: false,
|
|
187
|
+
isVariadic: true,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
else if (param.type === 'property_promotion_parameter') {
|
|
191
|
+
const nameNode = param.childForFieldName('name');
|
|
192
|
+
if (!nameNode)
|
|
193
|
+
continue;
|
|
194
|
+
const typeNode = param.childForFieldName('type');
|
|
195
|
+
const typeName = typeNode ? (extractSimpleTypeName(typeNode) ?? typeNode.text?.trim()) : null;
|
|
196
|
+
// Detect variadic: an ERROR child containing "..." indicates variadic promotion
|
|
197
|
+
let isVariadic = false;
|
|
198
|
+
for (let j = 0; j < param.childCount; j++) {
|
|
199
|
+
const c = param.child(j);
|
|
200
|
+
if (c && (c.text === '...' || (c.type === 'ERROR' && c.text === '...'))) {
|
|
201
|
+
isVariadic = true;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
params.push({
|
|
206
|
+
name: stripDollar(nameNode.text),
|
|
207
|
+
type: typeName ?? null,
|
|
208
|
+
rawType: typeNode?.text?.trim() ?? null,
|
|
209
|
+
isOptional: false,
|
|
210
|
+
isVariadic,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return params;
|
|
215
|
+
}
|
|
216
|
+
/** Strip leading $ from PHP variable names. */
|
|
217
|
+
function stripDollar(name) {
|
|
218
|
+
return name.startsWith('$') ? name.slice(1) : name;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Extract PHP 8 attributes (#[...]) from a method_declaration node.
|
|
222
|
+
*
|
|
223
|
+
* AST structure: attribute_list → attribute_group → attribute → name child.
|
|
224
|
+
* Names are prefixed with '#' to distinguish from Java/Kotlin @ annotations.
|
|
225
|
+
*/
|
|
226
|
+
function extractPhpAnnotations(node) {
|
|
227
|
+
const annotations = [];
|
|
228
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
229
|
+
const child = node.namedChild(i);
|
|
230
|
+
if (!child || child.type !== 'attribute_list')
|
|
231
|
+
continue;
|
|
232
|
+
for (let j = 0; j < child.namedChildCount; j++) {
|
|
233
|
+
const group = child.namedChild(j);
|
|
234
|
+
if (!group || group.type !== 'attribute_group')
|
|
235
|
+
continue;
|
|
236
|
+
for (let k = 0; k < group.namedChildCount; k++) {
|
|
237
|
+
const attr = group.namedChild(k);
|
|
238
|
+
if (!attr || attr.type !== 'attribute')
|
|
239
|
+
continue;
|
|
240
|
+
const nameNode = attr.firstNamedChild;
|
|
241
|
+
if (nameNode && nameNode.type === 'name') {
|
|
242
|
+
annotations.push('#' + nameNode.text);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return annotations;
|
|
248
|
+
}
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// PHP config
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
export const phpMethodConfig = {
|
|
253
|
+
language: SupportedLanguages.PHP,
|
|
254
|
+
typeDeclarationNodes: [
|
|
255
|
+
'class_declaration',
|
|
256
|
+
'interface_declaration',
|
|
257
|
+
'trait_declaration',
|
|
258
|
+
'enum_declaration',
|
|
259
|
+
],
|
|
260
|
+
methodNodeTypes: ['method_declaration', 'function_definition'],
|
|
261
|
+
bodyNodeTypes: ['declaration_list'],
|
|
262
|
+
extractName(node) {
|
|
263
|
+
return node.childForFieldName('name')?.text;
|
|
264
|
+
},
|
|
265
|
+
extractReturnType: extractPhpReturnType,
|
|
266
|
+
extractParameters: extractPhpParameters,
|
|
267
|
+
extractVisibility: findPhpVisibility,
|
|
268
|
+
isStatic(node) {
|
|
269
|
+
return hasModifierNode(node, 'static_modifier');
|
|
270
|
+
},
|
|
271
|
+
isAbstract(node, ownerNode) {
|
|
272
|
+
if (hasModifierNode(node, 'abstract_modifier'))
|
|
273
|
+
return true;
|
|
274
|
+
// Interface methods are implicitly abstract when they have no body.
|
|
275
|
+
// Check ownerNode first, then fall back to walking the parent chain
|
|
276
|
+
// (needed when called from extractFromNode where ownerNode === node).
|
|
277
|
+
let isInterface = ownerNode.type === 'interface_declaration';
|
|
278
|
+
if (!isInterface) {
|
|
279
|
+
let p = node.parent;
|
|
280
|
+
while (p) {
|
|
281
|
+
if (p.type === 'interface_declaration') {
|
|
282
|
+
isInterface = true;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
p = p.parent;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (isInterface) {
|
|
289
|
+
const body = node.childForFieldName('body');
|
|
290
|
+
if (body)
|
|
291
|
+
return false;
|
|
292
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
293
|
+
if (node.namedChild(i)?.type === 'compound_statement')
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
return false;
|
|
299
|
+
},
|
|
300
|
+
isFinal(node) {
|
|
301
|
+
return hasModifierNode(node, 'final_modifier');
|
|
302
|
+
},
|
|
303
|
+
extractAnnotations: extractPhpAnnotations,
|
|
304
|
+
};
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
// gitnexus/src/core/ingestion/method-extractors/configs/python.ts
|
|
2
|
+
// Verified against tree-sitter-python 0.23.4
|
|
3
|
+
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
4
|
+
import { hasKeyword } from '../../field-extractors/configs/helpers.js';
|
|
5
|
+
import { extractSimpleTypeName } from '../../type-extractors/shared.js';
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Python helpers
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
/** Names that represent the instance/class receiver — not real parameters. */
|
|
10
|
+
const SELF_NAMES = new Set(['self', 'cls']);
|
|
11
|
+
/**
|
|
12
|
+
* Unwrap a decorated_definition to its inner function_definition.
|
|
13
|
+
*
|
|
14
|
+
* tree-sitter-python wraps decorated functions/methods in a `decorated_definition`
|
|
15
|
+
* node that contains the decorators as children followed by the function_definition.
|
|
16
|
+
* This is different from TS/JS where decorators are siblings.
|
|
17
|
+
*/
|
|
18
|
+
function unwrapDecorated(node) {
|
|
19
|
+
if (node.type === 'decorated_definition') {
|
|
20
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
21
|
+
const child = node.namedChild(i);
|
|
22
|
+
if (child && child.type === 'function_definition')
|
|
23
|
+
return child;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return node;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Collect decorator names from a decorated_definition wrapper.
|
|
30
|
+
*
|
|
31
|
+
* Returns decorator names prefixed with '@'. If the node is a plain
|
|
32
|
+
* function_definition (no decorators), check if its parent is a
|
|
33
|
+
* decorated_definition and collect from there.
|
|
34
|
+
*/
|
|
35
|
+
function collectDecorators(node) {
|
|
36
|
+
let wrapper = null;
|
|
37
|
+
if (node.type === 'decorated_definition') {
|
|
38
|
+
wrapper = node;
|
|
39
|
+
}
|
|
40
|
+
else if (node.parent?.type === 'decorated_definition') {
|
|
41
|
+
wrapper = node.parent;
|
|
42
|
+
}
|
|
43
|
+
if (!wrapper)
|
|
44
|
+
return [];
|
|
45
|
+
const decorators = [];
|
|
46
|
+
for (let i = 0; i < wrapper.namedChildCount; i++) {
|
|
47
|
+
const child = wrapper.namedChild(i);
|
|
48
|
+
if (child && child.type === 'decorator') {
|
|
49
|
+
decorators.push(child);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return decorators;
|
|
53
|
+
}
|
|
54
|
+
function extractDecoratorName(decorator) {
|
|
55
|
+
// decorator > identifier (simple)
|
|
56
|
+
// decorator > call > identifier (call-style, e.g. @lru_cache())
|
|
57
|
+
// decorator > attribute (dotted, e.g. @abc.abstractmethod)
|
|
58
|
+
const expr = decorator.firstNamedChild;
|
|
59
|
+
if (!expr)
|
|
60
|
+
return undefined;
|
|
61
|
+
if (expr.type === 'identifier')
|
|
62
|
+
return '@' + expr.text;
|
|
63
|
+
if (expr.type === 'attribute')
|
|
64
|
+
return '@' + expr.text;
|
|
65
|
+
if (expr.type === 'call') {
|
|
66
|
+
const fn = expr.childForFieldName('function');
|
|
67
|
+
return fn ? '@' + fn.text : undefined;
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
function hasDecorator(node, name) {
|
|
72
|
+
const decorators = collectDecorators(node);
|
|
73
|
+
for (const dec of decorators) {
|
|
74
|
+
const decName = extractDecoratorName(dec);
|
|
75
|
+
if (decName === '@' + name || decName?.endsWith('.' + name))
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Extract parameters from a Python function_definition.
|
|
82
|
+
*
|
|
83
|
+
* Handles: identifier, default_parameter, typed_parameter, typed_default_parameter,
|
|
84
|
+
* list_splat_pattern (*args), dictionary_splat_pattern (**kwargs), and typed variants.
|
|
85
|
+
* Skips `self` and `cls` first parameters.
|
|
86
|
+
*/
|
|
87
|
+
function extractPythonParameters(node) {
|
|
88
|
+
const funcNode = unwrapDecorated(node);
|
|
89
|
+
const paramList = funcNode.childForFieldName('parameters');
|
|
90
|
+
if (!paramList)
|
|
91
|
+
return [];
|
|
92
|
+
const params = [];
|
|
93
|
+
let isFirst = true;
|
|
94
|
+
for (let i = 0; i < paramList.namedChildCount; i++) {
|
|
95
|
+
const param = paramList.namedChild(i);
|
|
96
|
+
if (!param)
|
|
97
|
+
continue;
|
|
98
|
+
switch (param.type) {
|
|
99
|
+
case 'identifier': {
|
|
100
|
+
// Bare parameter: `self`, `cls`, or untyped `x`
|
|
101
|
+
if (isFirst && SELF_NAMES.has(param.text)) {
|
|
102
|
+
isFirst = false;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
isFirst = false;
|
|
106
|
+
params.push({
|
|
107
|
+
name: param.text,
|
|
108
|
+
type: null,
|
|
109
|
+
rawType: null,
|
|
110
|
+
isOptional: false,
|
|
111
|
+
isVariadic: false,
|
|
112
|
+
});
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case 'default_parameter': {
|
|
116
|
+
// `x = value` — untyped with default
|
|
117
|
+
isFirst = false;
|
|
118
|
+
const nameNode = param.childForFieldName('name');
|
|
119
|
+
if (nameNode) {
|
|
120
|
+
params.push({
|
|
121
|
+
name: nameNode.text,
|
|
122
|
+
type: null,
|
|
123
|
+
rawType: null,
|
|
124
|
+
isOptional: true,
|
|
125
|
+
isVariadic: false,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'typed_parameter': {
|
|
131
|
+
// `x: int` or `*args: str` or `**kwargs: int`
|
|
132
|
+
// The first named child can be identifier, list_splat_pattern, or dictionary_splat_pattern
|
|
133
|
+
const inner = param.firstNamedChild;
|
|
134
|
+
if (!inner)
|
|
135
|
+
break;
|
|
136
|
+
if (isFirst && inner.type === 'identifier' && SELF_NAMES.has(inner.text)) {
|
|
137
|
+
isFirst = false;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
isFirst = false;
|
|
141
|
+
const typeNode = param.childForFieldName('type');
|
|
142
|
+
const typeText = typeNode
|
|
143
|
+
? (extractSimpleTypeName(typeNode) ?? typeNode.text?.trim() ?? null)
|
|
144
|
+
: null;
|
|
145
|
+
const rawTypeText = typeNode?.text?.trim() ?? null;
|
|
146
|
+
if (inner.type === 'list_splat_pattern') {
|
|
147
|
+
const nameId = inner.firstNamedChild;
|
|
148
|
+
if (nameId) {
|
|
149
|
+
params.push({
|
|
150
|
+
name: nameId.text,
|
|
151
|
+
type: typeText,
|
|
152
|
+
rawType: rawTypeText,
|
|
153
|
+
isOptional: false,
|
|
154
|
+
isVariadic: true,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else if (inner.type === 'dictionary_splat_pattern') {
|
|
159
|
+
const nameId = inner.firstNamedChild;
|
|
160
|
+
if (nameId) {
|
|
161
|
+
params.push({
|
|
162
|
+
name: nameId.text,
|
|
163
|
+
type: typeText,
|
|
164
|
+
rawType: rawTypeText,
|
|
165
|
+
isOptional: false,
|
|
166
|
+
isVariadic: true,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
params.push({
|
|
172
|
+
name: inner.text,
|
|
173
|
+
type: typeText,
|
|
174
|
+
rawType: rawTypeText,
|
|
175
|
+
isOptional: false,
|
|
176
|
+
isVariadic: false,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
case 'typed_default_parameter': {
|
|
182
|
+
// `x: int = 5` — typed with default
|
|
183
|
+
isFirst = false;
|
|
184
|
+
const nameNode = param.childForFieldName('name');
|
|
185
|
+
const typeNode = param.childForFieldName('type');
|
|
186
|
+
if (nameNode) {
|
|
187
|
+
params.push({
|
|
188
|
+
name: nameNode.text,
|
|
189
|
+
type: typeNode
|
|
190
|
+
? (extractSimpleTypeName(typeNode) ?? typeNode.text?.trim() ?? null)
|
|
191
|
+
: null,
|
|
192
|
+
rawType: typeNode?.text?.trim() ?? null,
|
|
193
|
+
isOptional: true,
|
|
194
|
+
isVariadic: false,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case 'list_splat_pattern': {
|
|
200
|
+
// `*args` (untyped)
|
|
201
|
+
isFirst = false;
|
|
202
|
+
const nameId = param.firstNamedChild;
|
|
203
|
+
if (nameId) {
|
|
204
|
+
params.push({
|
|
205
|
+
name: nameId.text,
|
|
206
|
+
type: null,
|
|
207
|
+
rawType: null,
|
|
208
|
+
isOptional: false,
|
|
209
|
+
isVariadic: true,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
case 'dictionary_splat_pattern': {
|
|
215
|
+
// `**kwargs` (untyped)
|
|
216
|
+
isFirst = false;
|
|
217
|
+
const nameId = param.firstNamedChild;
|
|
218
|
+
if (nameId) {
|
|
219
|
+
params.push({
|
|
220
|
+
name: nameId.text,
|
|
221
|
+
type: null,
|
|
222
|
+
rawType: null,
|
|
223
|
+
isOptional: false,
|
|
224
|
+
isVariadic: true,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
default:
|
|
230
|
+
isFirst = false;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return params;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Extract return type from the `return_type` field.
|
|
238
|
+
*
|
|
239
|
+
* tree-sitter-python uses a `type` field on function_definition for the return
|
|
240
|
+
* type annotation (e.g. `-> str`). The field contains a type node.
|
|
241
|
+
*/
|
|
242
|
+
function extractPythonReturnType(node) {
|
|
243
|
+
const funcNode = unwrapDecorated(node);
|
|
244
|
+
const returnType = funcNode.childForFieldName('return_type');
|
|
245
|
+
if (!returnType)
|
|
246
|
+
return undefined;
|
|
247
|
+
// Use .text to preserve full generic types (e.g. list[User], Dict[str, User])
|
|
248
|
+
// that the call resolver needs for for-loop iterable and return-type inference.
|
|
249
|
+
return returnType.text?.trim();
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Extract visibility based on Python name-mangling convention.
|
|
253
|
+
* `__name` (not dunder) = private, `_name` = protected, else public.
|
|
254
|
+
*/
|
|
255
|
+
function extractPythonVisibility(node) {
|
|
256
|
+
const funcNode = unwrapDecorated(node);
|
|
257
|
+
const nameNode = funcNode.childForFieldName('name');
|
|
258
|
+
const name = nameNode?.text;
|
|
259
|
+
if (!name)
|
|
260
|
+
return 'public';
|
|
261
|
+
if (name.startsWith('__') && !name.endsWith('__'))
|
|
262
|
+
return 'private';
|
|
263
|
+
if (name.startsWith('_') && !(name.startsWith('__') && name.endsWith('__')))
|
|
264
|
+
return 'protected';
|
|
265
|
+
return 'public';
|
|
266
|
+
}
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
// Config
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
export const pythonMethodConfig = {
|
|
271
|
+
language: SupportedLanguages.Python,
|
|
272
|
+
typeDeclarationNodes: ['class_definition'],
|
|
273
|
+
// Both function_definition and decorated_definition must be listed:
|
|
274
|
+
// decorated methods appear as decorated_definition in the class body block,
|
|
275
|
+
// while undecorated methods appear as function_definition directly.
|
|
276
|
+
methodNodeTypes: ['function_definition', 'decorated_definition'],
|
|
277
|
+
bodyNodeTypes: ['block'],
|
|
278
|
+
extractName(node) {
|
|
279
|
+
const funcNode = unwrapDecorated(node);
|
|
280
|
+
const nameNode = funcNode.childForFieldName('name');
|
|
281
|
+
return nameNode?.text;
|
|
282
|
+
},
|
|
283
|
+
extractReturnType: extractPythonReturnType,
|
|
284
|
+
extractParameters: extractPythonParameters,
|
|
285
|
+
extractVisibility: extractPythonVisibility,
|
|
286
|
+
isStatic(node) {
|
|
287
|
+
return hasDecorator(node, 'staticmethod') || hasDecorator(node, 'classmethod');
|
|
288
|
+
},
|
|
289
|
+
isAbstract(node, _ownerNode) {
|
|
290
|
+
return hasDecorator(node, 'abstractmethod');
|
|
291
|
+
},
|
|
292
|
+
isFinal(_node) {
|
|
293
|
+
return false; // @typing.final (PEP 591) is captured in annotations; isFinal not modeled
|
|
294
|
+
},
|
|
295
|
+
extractAnnotations(node) {
|
|
296
|
+
const decorators = collectDecorators(node);
|
|
297
|
+
const annotations = [];
|
|
298
|
+
for (const dec of decorators) {
|
|
299
|
+
const name = extractDecoratorName(dec);
|
|
300
|
+
if (name)
|
|
301
|
+
annotations.push(name);
|
|
302
|
+
}
|
|
303
|
+
return annotations;
|
|
304
|
+
},
|
|
305
|
+
isAsync(node) {
|
|
306
|
+
const funcNode = unwrapDecorated(node);
|
|
307
|
+
return hasKeyword(funcNode, 'async');
|
|
308
|
+
},
|
|
309
|
+
};
|