gitnexus 1.6.10-rc.87 → 1.6.10-rc.89
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/dist/core/ingestion/languages/python/captures.d.ts +3 -4
- package/dist/core/ingestion/languages/python/captures.js +5 -5
- package/dist/core/ingestion/languages/python/interpret.js +4 -0
- package/dist/core/ingestion/languages/python/receiver-binding.d.ts +15 -2
- package/dist/core/ingestion/languages/python/receiver-binding.js +96 -2
- package/dist/core/ingestion/languages/python/simple-hooks.d.ts +5 -5
- package/dist/core/ingestion/languages/python/simple-hooks.js +15 -5
- package/dist/core/ingestion/scope-extractor.js +5 -3
- package/package.json +2 -2
|
@@ -8,10 +8,9 @@
|
|
|
8
8
|
* 1. **Per-name import statements** — `import a, b` and
|
|
9
9
|
* `from m import x, y` decompose to one match per imported name
|
|
10
10
|
* (see `import-decomposer.ts`).
|
|
11
|
-
* 2. **Receiver type bindings** —
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* receiver (see `receiver-binding.ts`).
|
|
11
|
+
* 2. **Receiver type bindings** — methods emit an implicit `self` / `cls`
|
|
12
|
+
* binding, and `__init__` assignments from annotated parameters emit
|
|
13
|
+
* class-scoped instance-field bindings (see `receiver-binding.ts`).
|
|
15
14
|
*
|
|
16
15
|
* Pure given the input source text. No I/O, no globals consulted.
|
|
17
16
|
*/
|
|
@@ -8,17 +8,16 @@
|
|
|
8
8
|
* 1. **Per-name import statements** — `import a, b` and
|
|
9
9
|
* `from m import x, y` decompose to one match per imported name
|
|
10
10
|
* (see `import-decomposer.ts`).
|
|
11
|
-
* 2. **Receiver type bindings** —
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* receiver (see `receiver-binding.ts`).
|
|
11
|
+
* 2. **Receiver type bindings** — methods emit an implicit `self` / `cls`
|
|
12
|
+
* binding, and `__init__` assignments from annotated parameters emit
|
|
13
|
+
* class-scoped instance-field bindings (see `receiver-binding.ts`).
|
|
15
14
|
*
|
|
16
15
|
* Pure given the input source text. No I/O, no globals consulted.
|
|
17
16
|
*/
|
|
18
17
|
import { nodeToCapture, syntheticCapture, walkNamedTree, } from '../../utils/ast-helpers.js';
|
|
19
18
|
import { splitImportStatement } from './import-decomposer.js';
|
|
20
19
|
import { getPythonParser, getPythonScopeQuery } from './query.js';
|
|
21
|
-
import { synthesizeReceiverTypeBinding } from './receiver-binding.js';
|
|
20
|
+
import { synthesizeConstructorFieldTypeBindings, synthesizeReceiverTypeBinding, } from './receiver-binding.js';
|
|
22
21
|
import { synthesizeDependsReferences } from './depends-references.js';
|
|
23
22
|
import { computePythonArityMetadata } from './arity-metadata.js';
|
|
24
23
|
import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
|
|
@@ -122,6 +121,7 @@ export function emitPythonScopeCaptures(sourceText, _filePath, cachedTree) {
|
|
|
122
121
|
const synth = synthesizeReceiverTypeBinding(fnNode);
|
|
123
122
|
if (synth !== null)
|
|
124
123
|
out.push(synth);
|
|
124
|
+
out.push(...synthesizeConstructorFieldTypeBindings(fnNode));
|
|
125
125
|
for (const depRef of synthesizeDependsReferences(fnNode))
|
|
126
126
|
out.push(depRef);
|
|
127
127
|
}
|
|
@@ -118,6 +118,10 @@ export function interpretPythonTypeBinding(captures) {
|
|
|
118
118
|
// `Registry.lookup` Step 2 treats them identically.
|
|
119
119
|
else if (captures['@type-binding.cls'] !== undefined)
|
|
120
120
|
source = 'self';
|
|
121
|
+
else if (captures['@type-binding.instance-field'] !== undefined) {
|
|
122
|
+
source =
|
|
123
|
+
captures['@type-binding.parameter'] !== undefined ? 'parameter-annotation' : 'annotation';
|
|
124
|
+
}
|
|
121
125
|
else if (captures['@type-binding.constructor'] !== undefined)
|
|
122
126
|
source = 'constructor-inferred';
|
|
123
127
|
else if (captures['@type-binding.annotation'] !== undefined)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Synthesize
|
|
3
|
-
* methods.
|
|
2
|
+
* Synthesize implicit receiver and constructor-assigned field type bindings
|
|
3
|
+
* for methods.
|
|
4
4
|
*
|
|
5
5
|
* Tree-sitter can't easily express "the first parameter of a function
|
|
6
6
|
* defined directly inside a class body" via a single static query.
|
|
@@ -19,3 +19,16 @@ import { type SyntaxNode } from '../../utils/ast-helpers.js';
|
|
|
19
19
|
* 'function_definition'`.
|
|
20
20
|
*/
|
|
21
21
|
export declare function synthesizeReceiverTypeBinding(fnNode: SyntaxNode): CaptureMatch | null;
|
|
22
|
+
/**
|
|
23
|
+
* Synthesize class-scope field bindings for the common Python constructor
|
|
24
|
+
* injection pattern:
|
|
25
|
+
*
|
|
26
|
+
* def __init__(self, service: Service):
|
|
27
|
+
* self.service = service
|
|
28
|
+
*
|
|
29
|
+
* An explicit field annotation (`self.service: Service = ...`) is also
|
|
30
|
+
* accepted and takes precedence over a parameter annotation. Deliberately do
|
|
31
|
+
* not infer from arbitrary unannotated RHS expressions: the receiver resolver
|
|
32
|
+
* needs a declared type, not a name-only guess.
|
|
33
|
+
*/
|
|
34
|
+
export declare function synthesizeConstructorFieldTypeBindings(fnNode: SyntaxNode): CaptureMatch[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Synthesize
|
|
3
|
-
* methods.
|
|
2
|
+
* Synthesize implicit receiver and constructor-assigned field type bindings
|
|
3
|
+
* for methods.
|
|
4
4
|
*
|
|
5
5
|
* Tree-sitter can't easily express "the first parameter of a function
|
|
6
6
|
* defined directly inside a class body" via a single static query.
|
|
@@ -114,3 +114,97 @@ export function synthesizeReceiverTypeBinding(fnNode) {
|
|
|
114
114
|
'@type-binding.type': syntheticCapture('@type-binding.type', first, className),
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Synthesize class-scope field bindings for the common Python constructor
|
|
119
|
+
* injection pattern:
|
|
120
|
+
*
|
|
121
|
+
* def __init__(self, service: Service):
|
|
122
|
+
* self.service = service
|
|
123
|
+
*
|
|
124
|
+
* An explicit field annotation (`self.service: Service = ...`) is also
|
|
125
|
+
* accepted and takes precedence over a parameter annotation. Deliberately do
|
|
126
|
+
* not infer from arbitrary unannotated RHS expressions: the receiver resolver
|
|
127
|
+
* needs a declared type, not a name-only guess.
|
|
128
|
+
*/
|
|
129
|
+
export function synthesizeConstructorFieldTypeBindings(fnNode) {
|
|
130
|
+
if (fnNode.childForFieldName('name')?.text !== '__init__')
|
|
131
|
+
return [];
|
|
132
|
+
if (findEnclosingClassDefinition(fnNode) === null)
|
|
133
|
+
return [];
|
|
134
|
+
if (hasDecorator(fnNode, 'staticmethod') || hasDecorator(fnNode, 'classmethod'))
|
|
135
|
+
return [];
|
|
136
|
+
const receiver = synthesizeReceiverTypeBinding(fnNode);
|
|
137
|
+
const receiverName = receiver?.['@type-binding.self']?.text;
|
|
138
|
+
if (receiverName === undefined)
|
|
139
|
+
return [];
|
|
140
|
+
const parameters = fnNode.childForFieldName('parameters');
|
|
141
|
+
const body = fnNode.childForFieldName('body');
|
|
142
|
+
if (parameters === null || body === null)
|
|
143
|
+
return [];
|
|
144
|
+
const parameterTypes = new Map();
|
|
145
|
+
for (let i = 0; i < parameters.namedChildCount; i++) {
|
|
146
|
+
const parameter = parameters.namedChild(i);
|
|
147
|
+
if (parameter === null)
|
|
148
|
+
continue;
|
|
149
|
+
const name = firstParameterName(parameter);
|
|
150
|
+
const annotation = parameter.childForFieldName('type');
|
|
151
|
+
if (name !== null && annotation !== null)
|
|
152
|
+
parameterTypes.set(name, annotation.text);
|
|
153
|
+
}
|
|
154
|
+
const candidates = new Map();
|
|
155
|
+
const stack = [body];
|
|
156
|
+
while (stack.length > 0) {
|
|
157
|
+
const node = stack.pop();
|
|
158
|
+
if (node !== body &&
|
|
159
|
+
(node.type === 'function_definition' ||
|
|
160
|
+
node.type === 'lambda' ||
|
|
161
|
+
node.type === 'class_definition' ||
|
|
162
|
+
node.type === 'if_statement' ||
|
|
163
|
+
node.type === 'for_statement' ||
|
|
164
|
+
node.type === 'while_statement' ||
|
|
165
|
+
node.type === 'try_statement' ||
|
|
166
|
+
node.type === 'match_statement')) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (node.type === 'assignment') {
|
|
170
|
+
const left = node.childForFieldName('left');
|
|
171
|
+
const right = node.childForFieldName('right');
|
|
172
|
+
if (left?.type === 'attribute') {
|
|
173
|
+
const object = left.childForFieldName('object');
|
|
174
|
+
const field = left.childForFieldName('attribute');
|
|
175
|
+
if (object?.type === 'identifier' && object.text === receiverName && field !== null) {
|
|
176
|
+
const explicitType = node.childForFieldName('type');
|
|
177
|
+
const parameterType = right?.type === 'identifier' ? parameterTypes.get(right.text) : undefined;
|
|
178
|
+
const typeName = explicitType?.text ?? parameterType;
|
|
179
|
+
if (typeName !== undefined) {
|
|
180
|
+
const explicit = explicitType !== null;
|
|
181
|
+
const existing = candidates.get(field.text);
|
|
182
|
+
if (existing === undefined || explicit || !existing.explicit) {
|
|
183
|
+
candidates.set(field.text, {
|
|
184
|
+
explicit,
|
|
185
|
+
match: {
|
|
186
|
+
'@type-binding.name': syntheticCapture('@type-binding.name', field, field.text),
|
|
187
|
+
'@type-binding.type': syntheticCapture('@type-binding.type', explicitType ?? right ?? field, typeName),
|
|
188
|
+
...(explicit
|
|
189
|
+
? {}
|
|
190
|
+
: {
|
|
191
|
+
'@type-binding.parameter': syntheticCapture('@type-binding.parameter', right ?? field, '1'),
|
|
192
|
+
}),
|
|
193
|
+
'@type-binding.instance-field': syntheticCapture('@type-binding.instance-field', node, '1'),
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Push in reverse so the LIFO walk visits source order. That keeps Map
|
|
202
|
+
// insertion order (and therefore emitted capture order) deterministic.
|
|
203
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
204
|
+
const child = node.namedChild(i);
|
|
205
|
+
if (child !== null)
|
|
206
|
+
stack.push(child);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return [...candidates.values()].map(({ match }) => match);
|
|
210
|
+
}
|
|
@@ -9,11 +9,11 @@ import type { CaptureMatch, NodeLabel, ParsedImport, Scope, ScopeId, ScopeTree,
|
|
|
9
9
|
import type { SyntaxNode } from 'tree-sitter';
|
|
10
10
|
export declare function pythonFunctionDefinitionLabel(functionNode: SyntaxNode, defaultLabel: NodeLabel): NodeLabel;
|
|
11
11
|
/** Python has no block scope, so the central extractor's "innermost
|
|
12
|
-
* enclosing scope" default is already correct
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
export declare function pythonBindingScopeFor(
|
|
12
|
+
* enclosing scope" default is already correct for ordinary bindings.
|
|
13
|
+
* Constructor-injected instance fields are the exception: their marker is
|
|
14
|
+
* anchored inside `__init__`, but compound receiver resolution needs the
|
|
15
|
+
* field type on the enclosing Class scope. */
|
|
16
|
+
export declare function pythonBindingScopeFor(decl: CaptureMatch, innermost: Scope, tree: ScopeTree): ScopeId | null;
|
|
17
17
|
/** Function-local `from x import Y` should attach the binding to the
|
|
18
18
|
* function scope, not the module. Class-body imports (rare but legal —
|
|
19
19
|
* `class A: import x` makes `x` a class attribute) attach to the class.
|
|
@@ -15,11 +15,21 @@ export function pythonFunctionDefinitionLabel(functionNode, defaultLabel) {
|
|
|
15
15
|
}
|
|
16
16
|
// ─── bindingScopeFor ──────────────────────────────────────────────────────
|
|
17
17
|
/** Python has no block scope, so the central extractor's "innermost
|
|
18
|
-
* enclosing scope" default is already correct
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
export function pythonBindingScopeFor(
|
|
18
|
+
* enclosing scope" default is already correct for ordinary bindings.
|
|
19
|
+
* Constructor-injected instance fields are the exception: their marker is
|
|
20
|
+
* anchored inside `__init__`, but compound receiver resolution needs the
|
|
21
|
+
* field type on the enclosing Class scope. */
|
|
22
|
+
export function pythonBindingScopeFor(decl, innermost, tree) {
|
|
23
|
+
if (decl['@type-binding.instance-field'] !== undefined) {
|
|
24
|
+
let current = innermost;
|
|
25
|
+
while (current !== undefined) {
|
|
26
|
+
if (current.kind === 'Class')
|
|
27
|
+
return current.id;
|
|
28
|
+
if (current.parent === null)
|
|
29
|
+
break;
|
|
30
|
+
current = tree.getScope(current.parent);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
23
33
|
return null;
|
|
24
34
|
}
|
|
25
35
|
// ─── importOwningScope ────────────────────────────────────────────────────
|
|
@@ -770,13 +770,15 @@ function followChainedRef(start, draftById) {
|
|
|
770
770
|
* name in the same scope. Higher number wins; ties keep the later match
|
|
771
771
|
* (last-write-wins preserves historical order within a tier).
|
|
772
772
|
*
|
|
773
|
-
* Rationale: explicit annotations always beat
|
|
774
|
-
*
|
|
775
|
-
*
|
|
773
|
+
* Rationale: explicit variable and field annotations always beat bindings
|
|
774
|
+
* derived from parameter annotations or inference because they reflect the
|
|
775
|
+
* most specific user intent. `self`/`cls` are treated as strongly as other
|
|
776
|
+
* declared types because they are language-required receiver types.
|
|
776
777
|
*/
|
|
777
778
|
function typeBindingStrength(source) {
|
|
778
779
|
switch (source) {
|
|
779
780
|
case 'annotation':
|
|
781
|
+
return 3;
|
|
780
782
|
case 'parameter-annotation':
|
|
781
783
|
case 'return-annotation':
|
|
782
784
|
case 'self':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitnexus",
|
|
3
|
-
"version": "1.6.10-rc.
|
|
3
|
+
"version": "1.6.10-rc.89",
|
|
4
4
|
"description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
|
|
5
5
|
"author": "Abhigyan Patwari",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"version": "node scripts/sync-plugin-manifests.mjs"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@ladybugdb/core": "^0.18.
|
|
59
|
+
"@ladybugdb/core": "^0.18.3",
|
|
60
60
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
61
61
|
"@scarf/scarf": "^1.4.0",
|
|
62
62
|
"busboy": "^1.6.0",
|