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,285 @@
|
|
|
1
|
+
// gitnexus/src/core/ingestion/method-extractors/configs/ruby.ts
|
|
2
|
+
// Verified against tree-sitter-ruby 0.23.1
|
|
3
|
+
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Ruby helpers
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
const VISIBILITY_MODIFIERS = new Set(['private', 'protected', 'public']);
|
|
8
|
+
/** Regex to extract YARD `@return [Type]` annotations from comments. */
|
|
9
|
+
const YARD_RETURN_RE = /@return\s+\[([^\]]+)\]/;
|
|
10
|
+
/**
|
|
11
|
+
* Extract the simple type name from a YARD type string.
|
|
12
|
+
* Handles qualified types ("Models::User" -> "User"), generics ("Array<User>"
|
|
13
|
+
* -> "Array"), nullable ("String, nil" -> "String"), and rejects ambiguous
|
|
14
|
+
* unions ("String, Integer" -> undefined).
|
|
15
|
+
*/
|
|
16
|
+
function extractYardTypeName(yardType) {
|
|
17
|
+
const trimmed = yardType.trim();
|
|
18
|
+
// Bracket-balanced split on commas to handle generics like Hash<Symbol, User>
|
|
19
|
+
const parts = [];
|
|
20
|
+
let depth = 0, start = 0;
|
|
21
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
22
|
+
if (trimmed[i] === '<')
|
|
23
|
+
depth++;
|
|
24
|
+
else if (trimmed[i] === '>')
|
|
25
|
+
depth--;
|
|
26
|
+
else if (trimmed[i] === ',' && depth === 0) {
|
|
27
|
+
parts.push(trimmed.slice(start, i).trim());
|
|
28
|
+
start = i + 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
parts.push(trimmed.slice(start).trim());
|
|
32
|
+
const filtered = parts.filter((p) => p !== '' && p !== 'nil');
|
|
33
|
+
if (filtered.length !== 1)
|
|
34
|
+
return undefined; // ambiguous union
|
|
35
|
+
const typePart = filtered[0];
|
|
36
|
+
// Qualified: "Models::User" -> "User"
|
|
37
|
+
const segments = typePart.split('::');
|
|
38
|
+
const last = segments[segments.length - 1];
|
|
39
|
+
// Generic: "Array<User>" -> "Array"
|
|
40
|
+
const genericMatch = last.match(/^(\w+)\s*[<{(]/);
|
|
41
|
+
if (genericMatch)
|
|
42
|
+
return genericMatch[1];
|
|
43
|
+
// Simple identifier
|
|
44
|
+
if (/^\w+$/.test(last))
|
|
45
|
+
return last;
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Extract visibility for a Ruby method by walking backwards through the
|
|
50
|
+
* parent body_statement's named children from the method node's position.
|
|
51
|
+
*
|
|
52
|
+
* Ruby visibility modifiers (private, protected, public) appear as bare
|
|
53
|
+
* `identifier` nodes in the body_statement. The most recent modifier
|
|
54
|
+
* before the method determines its visibility. Default is public.
|
|
55
|
+
*
|
|
56
|
+
* Example AST for:
|
|
57
|
+
* class Foo
|
|
58
|
+
* private
|
|
59
|
+
* def secret; end
|
|
60
|
+
* end
|
|
61
|
+
*
|
|
62
|
+
* body_statement
|
|
63
|
+
* identifier "private" ← index 0
|
|
64
|
+
* method "def secret" ← index 1
|
|
65
|
+
*/
|
|
66
|
+
function extractRubyVisibility(node) {
|
|
67
|
+
const parent = node.parent;
|
|
68
|
+
if (!parent)
|
|
69
|
+
return 'public';
|
|
70
|
+
// Find the index of this method node in the parent's named children
|
|
71
|
+
let methodIndex = -1;
|
|
72
|
+
for (let i = 0; i < parent.namedChildCount; i++) {
|
|
73
|
+
if (parent.namedChild(i) === node) {
|
|
74
|
+
methodIndex = i;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (methodIndex < 0)
|
|
79
|
+
return 'public';
|
|
80
|
+
// Walk backwards from the method node looking for a visibility modifier
|
|
81
|
+
for (let i = methodIndex - 1; i >= 0; i--) {
|
|
82
|
+
const sibling = parent.namedChild(i);
|
|
83
|
+
if (!sibling)
|
|
84
|
+
continue;
|
|
85
|
+
if (sibling.type === 'identifier' && VISIBILITY_MODIFIERS.has(sibling.text)) {
|
|
86
|
+
return sibling.text;
|
|
87
|
+
}
|
|
88
|
+
// module_function makes instance methods private
|
|
89
|
+
if (sibling.type === 'identifier' && sibling.text === 'module_function') {
|
|
90
|
+
return 'private';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return 'public';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Extract parameters from a Ruby method's method_parameters node.
|
|
97
|
+
*
|
|
98
|
+
* Handles: identifier, optional_parameter (default), splat_parameter (*args),
|
|
99
|
+
* hash_splat_parameter (**kwargs), block_parameter (&block), keyword_parameter.
|
|
100
|
+
*/
|
|
101
|
+
function extractRubyParameters(node) {
|
|
102
|
+
const paramList = node.childForFieldName('parameters');
|
|
103
|
+
if (!paramList)
|
|
104
|
+
return [];
|
|
105
|
+
const params = [];
|
|
106
|
+
for (let i = 0; i < paramList.namedChildCount; i++) {
|
|
107
|
+
const param = paramList.namedChild(i);
|
|
108
|
+
if (!param)
|
|
109
|
+
continue;
|
|
110
|
+
switch (param.type) {
|
|
111
|
+
case 'identifier': {
|
|
112
|
+
// Plain parameter: def foo(x)
|
|
113
|
+
params.push({
|
|
114
|
+
name: param.text,
|
|
115
|
+
type: null,
|
|
116
|
+
rawType: null,
|
|
117
|
+
isOptional: false,
|
|
118
|
+
isVariadic: false,
|
|
119
|
+
});
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case 'optional_parameter': {
|
|
123
|
+
// Default parameter: def foo(x = 10)
|
|
124
|
+
const nameNode = param.childForFieldName('name');
|
|
125
|
+
if (nameNode) {
|
|
126
|
+
params.push({
|
|
127
|
+
name: nameNode.text,
|
|
128
|
+
type: null,
|
|
129
|
+
rawType: null,
|
|
130
|
+
isOptional: true,
|
|
131
|
+
isVariadic: false,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case 'splat_parameter': {
|
|
137
|
+
// Splat: def foo(*args)
|
|
138
|
+
const nameNode = param.childForFieldName('name');
|
|
139
|
+
if (nameNode) {
|
|
140
|
+
params.push({
|
|
141
|
+
name: nameNode.text,
|
|
142
|
+
type: null,
|
|
143
|
+
rawType: null,
|
|
144
|
+
isOptional: false,
|
|
145
|
+
isVariadic: true,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case 'hash_splat_parameter': {
|
|
151
|
+
// Double splat: def foo(**kwargs)
|
|
152
|
+
const nameNode = param.childForFieldName('name');
|
|
153
|
+
if (nameNode) {
|
|
154
|
+
params.push({
|
|
155
|
+
name: nameNode.text,
|
|
156
|
+
type: null,
|
|
157
|
+
rawType: null,
|
|
158
|
+
isOptional: false,
|
|
159
|
+
isVariadic: true,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case 'block_parameter': {
|
|
165
|
+
// Block: def foo(&block)
|
|
166
|
+
const nameNode = param.childForFieldName('name');
|
|
167
|
+
if (nameNode) {
|
|
168
|
+
params.push({
|
|
169
|
+
name: nameNode.text,
|
|
170
|
+
type: null,
|
|
171
|
+
rawType: null,
|
|
172
|
+
isOptional: false,
|
|
173
|
+
isVariadic: false,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
case 'keyword_parameter': {
|
|
179
|
+
// Keyword: def foo(name:) or def foo(name: "default")
|
|
180
|
+
const nameNode = param.childForFieldName('name');
|
|
181
|
+
const valueNode = param.childForFieldName('value');
|
|
182
|
+
if (nameNode) {
|
|
183
|
+
params.push({
|
|
184
|
+
name: nameNode.text,
|
|
185
|
+
type: null,
|
|
186
|
+
rawType: null,
|
|
187
|
+
isOptional: !!valueNode,
|
|
188
|
+
isVariadic: false,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return params;
|
|
196
|
+
}
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
// Config
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
export const rubyMethodConfig = {
|
|
201
|
+
language: SupportedLanguages.Ruby,
|
|
202
|
+
typeDeclarationNodes: ['class', 'module', 'singleton_class'],
|
|
203
|
+
methodNodeTypes: ['method', 'singleton_method'],
|
|
204
|
+
bodyNodeTypes: ['body_statement'],
|
|
205
|
+
extractOwnerName(node) {
|
|
206
|
+
// singleton_class (class << self) inherits the enclosing class/module name
|
|
207
|
+
if (node.type === 'singleton_class') {
|
|
208
|
+
let ancestor = node.parent;
|
|
209
|
+
while (ancestor) {
|
|
210
|
+
if (ancestor.type === 'class' || ancestor.type === 'module') {
|
|
211
|
+
const nameNode = ancestor.childForFieldName('name');
|
|
212
|
+
return nameNode?.text;
|
|
213
|
+
}
|
|
214
|
+
ancestor = ancestor.parent;
|
|
215
|
+
}
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
return undefined; // use default resolution for class/module
|
|
219
|
+
},
|
|
220
|
+
extractName(node) {
|
|
221
|
+
const nameNode = node.childForFieldName('name');
|
|
222
|
+
return nameNode?.text;
|
|
223
|
+
},
|
|
224
|
+
extractReturnType(node) {
|
|
225
|
+
// Walk backwards through preceding siblings looking for YARD @return [Type].
|
|
226
|
+
// Try direct siblings first, then fall back to parent (body_statement) siblings
|
|
227
|
+
// for class methods where the comment may be a sibling of the body_statement.
|
|
228
|
+
const search = (startNode) => {
|
|
229
|
+
let sibling = startNode.previousSibling;
|
|
230
|
+
while (sibling) {
|
|
231
|
+
if (sibling.type === 'comment') {
|
|
232
|
+
const match = YARD_RETURN_RE.exec(sibling.text);
|
|
233
|
+
if (match)
|
|
234
|
+
return extractYardTypeName(match[1]);
|
|
235
|
+
}
|
|
236
|
+
else if (sibling.isNamed) {
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
sibling = sibling.previousSibling;
|
|
240
|
+
}
|
|
241
|
+
return undefined;
|
|
242
|
+
};
|
|
243
|
+
const result = search(node);
|
|
244
|
+
if (result)
|
|
245
|
+
return result;
|
|
246
|
+
if (node.parent?.type === 'body_statement') {
|
|
247
|
+
return search(node.parent);
|
|
248
|
+
}
|
|
249
|
+
return undefined;
|
|
250
|
+
},
|
|
251
|
+
extractParameters: extractRubyParameters,
|
|
252
|
+
extractVisibility: extractRubyVisibility,
|
|
253
|
+
isStatic(node) {
|
|
254
|
+
if (node.type === 'singleton_method')
|
|
255
|
+
return true;
|
|
256
|
+
// module_function makes following methods callable at module level (static)
|
|
257
|
+
const parent = node.parent;
|
|
258
|
+
if (!parent)
|
|
259
|
+
return false;
|
|
260
|
+
let methodIndex = -1;
|
|
261
|
+
for (let i = 0; i < parent.namedChildCount; i++) {
|
|
262
|
+
if (parent.namedChild(i) === node) {
|
|
263
|
+
methodIndex = i;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
for (let i = methodIndex - 1; i >= 0; i--) {
|
|
268
|
+
const sibling = parent.namedChild(i);
|
|
269
|
+
if (!sibling)
|
|
270
|
+
continue;
|
|
271
|
+
if (sibling.type === 'identifier' && sibling.text === 'module_function')
|
|
272
|
+
return true;
|
|
273
|
+
// Other visibility modifiers override module_function
|
|
274
|
+
if (sibling.type === 'identifier' && VISIBILITY_MODIFIERS.has(sibling.text))
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
},
|
|
279
|
+
isAbstract(_node, _ownerNode) {
|
|
280
|
+
return false; // Ruby has no abstract methods
|
|
281
|
+
},
|
|
282
|
+
isFinal(_node) {
|
|
283
|
+
return false; // Ruby has no final methods
|
|
284
|
+
},
|
|
285
|
+
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// gitnexus/src/core/ingestion/method-extractors/configs/rust.ts
|
|
2
|
+
// Verified against tree-sitter-rust 0.23.1
|
|
3
|
+
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
4
|
+
import { extractSimpleTypeName } from '../../type-extractors/shared.js';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Rust helpers
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
/**
|
|
9
|
+
* Extract method name from function_item or function_signature_item.
|
|
10
|
+
* Both use a `name` field containing an identifier.
|
|
11
|
+
*/
|
|
12
|
+
function extractRustMethodName(node) {
|
|
13
|
+
const nameNode = node.childForFieldName('name');
|
|
14
|
+
return nameNode?.text;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Extract return type from the `return_type` field.
|
|
18
|
+
* tree-sitter-rust puts the return type (after `->`) as the `return_type` field.
|
|
19
|
+
*/
|
|
20
|
+
function extractRustReturnType(node) {
|
|
21
|
+
const typeNode = node.childForFieldName('return_type');
|
|
22
|
+
if (!typeNode)
|
|
23
|
+
return undefined;
|
|
24
|
+
return typeNode.text?.trim();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Extract parameters, skipping the self_parameter (handled by extractReceiverType).
|
|
28
|
+
*
|
|
29
|
+
* Rust parameters use `pattern` and `type` fields:
|
|
30
|
+
* parameter { pattern: identifier, type: primitive_type }
|
|
31
|
+
*/
|
|
32
|
+
function extractRustParameters(node) {
|
|
33
|
+
const paramList = node.childForFieldName('parameters');
|
|
34
|
+
if (!paramList)
|
|
35
|
+
return [];
|
|
36
|
+
const params = [];
|
|
37
|
+
for (let i = 0; i < paramList.namedChildCount; i++) {
|
|
38
|
+
const param = paramList.namedChild(i);
|
|
39
|
+
if (!param)
|
|
40
|
+
continue;
|
|
41
|
+
// Skip self_parameter — it is the receiver, not a regular parameter
|
|
42
|
+
if (param.type === 'self_parameter')
|
|
43
|
+
continue;
|
|
44
|
+
if (param.type === 'parameter') {
|
|
45
|
+
const patternNode = param.childForFieldName('pattern');
|
|
46
|
+
const typeNode = param.childForFieldName('type');
|
|
47
|
+
params.push({
|
|
48
|
+
name: patternNode?.text ?? '?',
|
|
49
|
+
type: typeNode ? (extractSimpleTypeName(typeNode) ?? typeNode.text?.trim() ?? null) : null,
|
|
50
|
+
rawType: typeNode?.text?.trim() ?? null,
|
|
51
|
+
isOptional: false,
|
|
52
|
+
isVariadic: false,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return params;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Detect visibility from visibility_modifier named child.
|
|
60
|
+
* `pub`, `pub(crate)`, `pub(super)`, `pub(in path)` → public.
|
|
61
|
+
* Absence → private (Rust default).
|
|
62
|
+
*/
|
|
63
|
+
function extractRustVisibility(node) {
|
|
64
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
65
|
+
const child = node.namedChild(i);
|
|
66
|
+
if (child?.type === 'visibility_modifier')
|
|
67
|
+
return 'public';
|
|
68
|
+
}
|
|
69
|
+
return 'private';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Detect receiver type from the first parameter if it is a self_parameter.
|
|
73
|
+
*
|
|
74
|
+
* Variants:
|
|
75
|
+
* - `self` → "self"
|
|
76
|
+
* - `&self` → "&self"
|
|
77
|
+
* - `&mut self` → "&mut self"
|
|
78
|
+
* - `mut self` → "mut self"
|
|
79
|
+
* - `self: Box<Self>` → "Box<Self>" (explicit self type)
|
|
80
|
+
*/
|
|
81
|
+
function extractRustReceiverType(node) {
|
|
82
|
+
const paramList = node.childForFieldName('parameters');
|
|
83
|
+
if (!paramList)
|
|
84
|
+
return undefined;
|
|
85
|
+
const first = paramList.namedChild(0);
|
|
86
|
+
if (!first || first.type !== 'self_parameter')
|
|
87
|
+
return undefined;
|
|
88
|
+
return first.text;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check whether a function_item has the `async` keyword.
|
|
92
|
+
* tree-sitter-rust wraps it in a `function_modifiers` named child.
|
|
93
|
+
*/
|
|
94
|
+
function isRustAsync(node) {
|
|
95
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
96
|
+
const child = node.namedChild(i);
|
|
97
|
+
if (child?.type === 'function_modifiers' && child.text.includes('async'))
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Extract attributes from preceding sibling attribute_item nodes.
|
|
104
|
+
*
|
|
105
|
+
* In tree-sitter-rust, `#[inline]` is an `attribute_item` sibling that precedes
|
|
106
|
+
* the function_item in the declaration_list, not a child of the function_item.
|
|
107
|
+
*/
|
|
108
|
+
function extractRustAnnotations(node) {
|
|
109
|
+
const annotations = [];
|
|
110
|
+
let sibling = node.previousNamedSibling;
|
|
111
|
+
while (sibling) {
|
|
112
|
+
if (sibling.type === 'attribute_item') {
|
|
113
|
+
annotations.unshift(sibling.text);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Stop at the first non-attribute sibling — attributes are contiguous
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
sibling = sibling.previousNamedSibling;
|
|
120
|
+
}
|
|
121
|
+
return annotations;
|
|
122
|
+
}
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// Rust config
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// Rust methods live inside `impl` blocks (concrete implementations) or
|
|
127
|
+
// `trait` blocks (trait definitions with required/default methods).
|
|
128
|
+
//
|
|
129
|
+
// `impl_item` body contains `function_item` nodes for concrete methods.
|
|
130
|
+
// `trait_item` body contains `function_item` (default methods) and
|
|
131
|
+
// `function_signature_item` (required/abstract methods without a body).
|
|
132
|
+
//
|
|
133
|
+
// ownerName resolution: `impl_item` has no `name` field — the generic
|
|
134
|
+
// extractor falls back to the first `type_identifier` child, which is the
|
|
135
|
+
// implementing type (e.g. `impl MyStruct { ... }` → "MyStruct").
|
|
136
|
+
// `trait_item` uses the standard `name` field.
|
|
137
|
+
//
|
|
138
|
+
// Known gaps:
|
|
139
|
+
// - Macro-generated methods (e.g. derive) are not visible in the AST.
|
|
140
|
+
// - Unsafe methods are not distinguished (no isUnsafe field in schema).
|
|
141
|
+
export const rustMethodConfig = {
|
|
142
|
+
language: SupportedLanguages.Rust,
|
|
143
|
+
typeDeclarationNodes: ['impl_item', 'trait_item'],
|
|
144
|
+
methodNodeTypes: ['function_item', 'function_signature_item'],
|
|
145
|
+
bodyNodeTypes: ['declaration_list'],
|
|
146
|
+
// For `impl Trait for Struct`, resolve owner to the concrete Struct (after `for`).
|
|
147
|
+
// For plain `impl Struct`, resolve to Struct (first type_identifier).
|
|
148
|
+
// For `trait Foo`, let the default name-field resolution handle it.
|
|
149
|
+
extractOwnerName(node) {
|
|
150
|
+
if (node.type !== 'impl_item')
|
|
151
|
+
return undefined;
|
|
152
|
+
const children = node.children ?? [];
|
|
153
|
+
const forIdx = children.findIndex((c) => c.text === 'for');
|
|
154
|
+
if (forIdx !== -1) {
|
|
155
|
+
// impl Trait for Struct — pick the type after `for`
|
|
156
|
+
const typeNode = children
|
|
157
|
+
.slice(forIdx + 1)
|
|
158
|
+
.find((c) => c.type === 'type_identifier' || c.type === 'scoped_type_identifier');
|
|
159
|
+
if (typeNode)
|
|
160
|
+
return typeNode.text;
|
|
161
|
+
}
|
|
162
|
+
// Plain `impl Struct` — pick the first type_identifier
|
|
163
|
+
const first = children.find((c) => c.type === 'type_identifier');
|
|
164
|
+
return first?.text;
|
|
165
|
+
},
|
|
166
|
+
extractName: extractRustMethodName,
|
|
167
|
+
extractReturnType: extractRustReturnType,
|
|
168
|
+
extractParameters: extractRustParameters,
|
|
169
|
+
extractVisibility: extractRustVisibility,
|
|
170
|
+
isStatic(node) {
|
|
171
|
+
// A Rust method is an "associated function" (static) if it lacks a
|
|
172
|
+
// self_parameter as first parameter.
|
|
173
|
+
const paramList = node.childForFieldName('parameters');
|
|
174
|
+
if (!paramList)
|
|
175
|
+
return true;
|
|
176
|
+
const first = paramList.namedChild(0);
|
|
177
|
+
return !first || first.type !== 'self_parameter';
|
|
178
|
+
},
|
|
179
|
+
isAbstract(node, ownerNode) {
|
|
180
|
+
// Only trait methods without a body (function_signature_item) are abstract.
|
|
181
|
+
// function_signature_item never has a body field.
|
|
182
|
+
if (ownerNode.type === 'trait_item' && node.type === 'function_signature_item') {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
},
|
|
187
|
+
isFinal() {
|
|
188
|
+
// Rust has no `final` concept — all methods are effectively sealed
|
|
189
|
+
// (traits cannot be "overridden" the way Java methods can).
|
|
190
|
+
return false;
|
|
191
|
+
},
|
|
192
|
+
extractAnnotations: extractRustAnnotations,
|
|
193
|
+
extractReceiverType: extractRustReceiverType,
|
|
194
|
+
isAsync: isRustAsync,
|
|
195
|
+
};
|