gitnexus 1.6.3-rc.8 → 1.6.3
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 +21 -5
- package/dist/_shared/graph/types.d.ts +16 -0
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +20 -2
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +11 -0
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/scope-resolution/def-index.js +2 -2
- package/dist/_shared/scope-resolution/def-index.js.map +1 -1
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +8 -0
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/method-dispatch-index.js +2 -2
- package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -1
- package/dist/_shared/scope-resolution/module-scope-index.d.ts +8 -0
- package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/module-scope-index.js +10 -2
- package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -1
- package/dist/_shared/scope-resolution/parsed-file.d.ts +76 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/parsed-file.js +54 -0
- package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
- package/dist/_shared/scope-resolution/position-index.d.ts +12 -0
- package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/position-index.js +2 -2
- package/dist/_shared/scope-resolution/position-index.js.map +1 -1
- package/dist/_shared/scope-resolution/qualified-name-index.js +2 -2
- package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -1
- package/dist/_shared/scope-resolution/reference-site.d.ts +75 -0
- package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/reference-site.js +24 -0
- package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/class-registry.d.ts +27 -0
- package/dist/_shared/scope-resolution/registries/class-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/class-registry.js +30 -0
- package/dist/_shared/scope-resolution/registries/class-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/context.d.ts +69 -0
- package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/context.js +44 -0
- package/dist/_shared/scope-resolution/registries/context.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/evidence.d.ts +56 -0
- package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/evidence.js +150 -0
- package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/field-registry.d.ts +26 -0
- package/dist/_shared/scope-resolution/registries/field-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/field-registry.js +31 -0
- package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +81 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.js +332 -0
- package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts +33 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.js +56 -0
- package/dist/_shared/scope-resolution/registries/lookup-qualified.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/method-registry.d.ts +36 -0
- package/dist/_shared/scope-resolution/registries/method-registry.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/method-registry.js +32 -0
- package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts +43 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.js +60 -0
- package/dist/_shared/scope-resolution/registries/tie-breaks.js.map +1 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +1 -10
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/resolve-type-ref.js +6 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts +4 -4
- package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.js +3 -2
- package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +6 -2
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/shadow/aggregate.js +5 -0
- package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +11 -0
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/cli/ai-context.js +35 -4
- package/dist/cli/analyze.d.ts +27 -0
- package/dist/cli/analyze.js +31 -1
- package/dist/cli/clean.js +19 -1
- package/dist/cli/group.js +73 -0
- package/dist/cli/index-repo.js +8 -1
- package/dist/cli/index.js +26 -1
- package/dist/cli/list.js +11 -1
- package/dist/cli/remove.d.ts +30 -0
- package/dist/cli/remove.js +99 -0
- package/dist/cli/setup.js +185 -57
- package/dist/cli/tool.d.ts +5 -0
- package/dist/cli/tool.js +42 -0
- package/dist/config/ignore-service.d.ts +9 -0
- package/dist/config/ignore-service.js +80 -13
- package/dist/core/embedding-mode.d.ts +30 -0
- package/dist/core/embedding-mode.js +30 -0
- package/dist/core/embeddings/ast-utils.js +22 -22
- package/dist/core/embeddings/chunker.js +30 -25
- package/dist/core/embeddings/embedding-pipeline.d.ts +6 -0
- package/dist/core/embeddings/embedding-pipeline.js +15 -6
- package/dist/core/embeddings/text-generator.d.ts +1 -1
- package/dist/core/embeddings/text-generator.js +33 -24
- package/dist/core/embeddings/types.d.ts +43 -1
- package/dist/core/embeddings/types.js +101 -29
- package/dist/core/git-staleness.d.ts +18 -0
- package/dist/core/git-staleness.js +108 -0
- package/dist/core/graph/graph.js +115 -20
- package/dist/core/graph/types.d.ts +12 -1
- package/dist/core/group/config-parser.d.ts +4 -0
- package/dist/core/group/config-parser.js +18 -1
- package/dist/core/group/cross-impact.d.ts +41 -0
- package/dist/core/group/cross-impact.js +441 -0
- package/dist/core/group/extractors/http-patterns/php.js +126 -18
- package/dist/core/group/group-path-utils.d.ts +17 -0
- package/dist/core/group/group-path-utils.js +40 -0
- package/dist/core/group/resolve-at-member.d.ts +10 -0
- package/dist/core/group/resolve-at-member.js +31 -0
- package/dist/core/group/service.d.ts +9 -0
- package/dist/core/group/service.js +259 -25
- package/dist/core/group/types.d.ts +30 -0
- package/dist/core/ingestion/ast-cache.d.ts +16 -1
- package/dist/core/ingestion/ast-cache.js +14 -2
- package/dist/core/ingestion/call-processor.js +9 -0
- package/dist/core/ingestion/emit-references.d.ts +88 -0
- package/dist/core/ingestion/emit-references.js +229 -0
- package/dist/core/ingestion/filesystem-walker.js +6 -4
- package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
- package/dist/core/ingestion/finalize-orchestrator.js +139 -0
- package/dist/core/ingestion/framework-detection.js +6 -2
- package/dist/core/ingestion/import-processor.js +4 -0
- package/dist/core/ingestion/import-resolvers/python.js +9 -6
- package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
- package/dist/core/ingestion/import-target-adapter.js +95 -0
- package/dist/core/ingestion/language-provider.d.ts +36 -33
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
- package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
- package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
- package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
- package/dist/core/ingestion/languages/csharp/arity.js +37 -0
- package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
- package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
- package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
- package/dist/core/ingestion/languages/csharp/captures.js +249 -0
- package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
- package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
- package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
- package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
- package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
- package/dist/core/ingestion/languages/csharp/index.js +82 -0
- package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
- package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
- package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
- package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +50 -0
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +374 -0
- package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
- package/dist/core/ingestion/languages/csharp/query.js +515 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
- package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
- package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
- package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
- package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
- package/dist/core/ingestion/languages/csharp.js +14 -0
- package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
- package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
- package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
- package/dist/core/ingestion/languages/python/arity.js +38 -0
- package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
- package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
- package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
- package/dist/core/ingestion/languages/python/captures.js +106 -0
- package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
- package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
- package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
- package/dist/core/ingestion/languages/python/import-target.js +99 -0
- package/dist/core/ingestion/languages/python/index.d.ts +80 -0
- package/dist/core/ingestion/languages/python/index.js +80 -0
- package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/python/interpret.js +191 -0
- package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
- package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
- package/dist/core/ingestion/languages/python/query.d.ts +9 -0
- package/dist/core/ingestion/languages/python/query.js +267 -0
- package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
- package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
- package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
- package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
- package/dist/core/ingestion/languages/python/simple-hooks.d.ts +23 -0
- package/dist/core/ingestion/languages/python/simple-hooks.js +35 -0
- package/dist/core/ingestion/languages/python.js +14 -0
- package/dist/core/ingestion/model/method-registry.d.ts +9 -0
- package/dist/core/ingestion/model/method-registry.js +4 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
- package/dist/core/ingestion/model/semantic-model.d.ts +64 -0
- package/dist/core/ingestion/model/semantic-model.js +55 -0
- package/dist/core/ingestion/mro-processor.js +38 -22
- package/dist/core/ingestion/parsing-processor.d.ts +18 -1
- package/dist/core/ingestion/parsing-processor.js +45 -11
- package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/index.js +1 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +10 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +17 -2
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +18 -0
- package/dist/core/ingestion/pipeline.js +2 -1
- package/dist/core/ingestion/registry-primary-flag.d.ts +86 -0
- package/dist/core/ingestion/registry-primary-flag.js +111 -0
- package/dist/core/ingestion/resolve-references.d.ts +63 -0
- package/dist/core/ingestion/resolve-references.js +175 -0
- package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
- package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
- package/dist/core/ingestion/scope-extractor.d.ts +86 -0
- package/dist/core/ingestion/scope-extractor.js +758 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +372 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +212 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +112 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +30 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +113 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +198 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +27 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +131 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +48 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +130 -0
- package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
- package/dist/core/ingestion/scope-resolution/passes/mro.js +99 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +26 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +61 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +327 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +130 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
- package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +21 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +66 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +157 -0
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +52 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +127 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +349 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
- package/dist/core/ingestion/shadow-harness.d.ts +113 -0
- package/dist/core/ingestion/shadow-harness.js +148 -0
- package/dist/core/ingestion/utils/ast-helpers.d.ts +19 -1
- package/dist/core/ingestion/utils/ast-helpers.js +70 -0
- package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
- package/dist/core/ingestion/utils/max-file-size.js +52 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +9 -0
- package/dist/core/ingestion/workers/parse-worker.js +57 -21
- package/dist/core/lbug/lbug-adapter.d.ts +22 -2
- package/dist/core/lbug/lbug-adapter.js +58 -14
- package/dist/core/lbug/pool-adapter.d.ts +17 -0
- package/dist/core/lbug/pool-adapter.js +24 -14
- package/dist/core/run-analyze.d.ts +32 -0
- package/dist/core/run-analyze.js +74 -19
- package/dist/core/search/bm25-index.d.ts +18 -0
- package/dist/core/search/bm25-index.js +125 -12
- package/dist/core/tree-sitter/parser-loader.js +6 -1
- package/dist/mcp/local/local-backend.d.ts +67 -3
- package/dist/mcp/local/local-backend.js +296 -34
- package/dist/mcp/resources.d.ts +31 -0
- package/dist/mcp/resources.js +100 -17
- package/dist/mcp/tools.d.ts +4 -1
- package/dist/mcp/tools.js +75 -54
- package/dist/server/api.js +6 -2
- package/dist/storage/git.d.ts +49 -0
- package/dist/storage/git.js +111 -0
- package/dist/storage/repo-manager.d.ts +246 -1
- package/dist/storage/repo-manager.js +391 -9
- package/package.json +7 -6
- package/scripts/bench-scope-resolution.ts +134 -0
- package/scripts/ci-list-migrated-languages.ts +24 -0
- package/skills/gitnexus-cli.md +1 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C# same-namespace cross-file visibility.
|
|
3
|
+
*
|
|
4
|
+
* C# makes every type declared in `namespace X` visible to every other
|
|
5
|
+
* file that also declares `namespace X`, without any explicit `using`
|
|
6
|
+
* directive. Python has no equivalent — every cross-file reference
|
|
7
|
+
* needs an explicit import — so this is a C#-specific pass.
|
|
8
|
+
*
|
|
9
|
+
* Without this: `Service.cs` (namespace `FieldTypes`) can't see
|
|
10
|
+
* `User` declared in `Models.cs` (same namespace), so `user.Address`
|
|
11
|
+
* field-chain resolution fails at `findClassBindingInScope('User')`
|
|
12
|
+
* in the Service.cs scope chain.
|
|
13
|
+
*
|
|
14
|
+
* Implementation: after the finalize pass populates `indexes.bindings`
|
|
15
|
+
* (from explicit `using` directives), walk each file's tree-sitter
|
|
16
|
+
* AST for `namespace_declaration` / `file_scoped_namespace_declaration`
|
|
17
|
+
* and `using_directive` nodes. The orchestrator hands us its
|
|
18
|
+
* `treeCache` so files already parsed by `extractParsedFile` are
|
|
19
|
+
* re-used instead of re-parsed — `ParsedFile`'s underlying tree is
|
|
20
|
+
* the single source of truth. Group classes by namespace, and inject
|
|
21
|
+
* cross-file sibling classes into each Namespace scope's finalized
|
|
22
|
+
* bindings with `origin: 'namespace'` — a tier below `local` so a
|
|
23
|
+
* local declaration still shadows a cross-file sibling with the same
|
|
24
|
+
* name.
|
|
25
|
+
*
|
|
26
|
+
* The tree-sitter walk is authoritative: it sees `global using static`,
|
|
27
|
+
* aliased `using static X = Y.Z;`, attributed namespace declarations,
|
|
28
|
+
* and preprocessor-guarded declarations correctly because the
|
|
29
|
+
* tree-sitter grammar parses them as real nodes (not textual
|
|
30
|
+
* coincidences).
|
|
31
|
+
*/
|
|
32
|
+
import { getCsharpParser } from './query.js';
|
|
33
|
+
/** Build a structural view of a C# file by walking the tree-sitter
|
|
34
|
+
* AST. Prefers `cachedTree` (handed in via `treeCache`) so we don't
|
|
35
|
+
* re-parse files the orchestrator already parsed for `extractParsedFile`;
|
|
36
|
+
* falls back to a fresh parse on cache miss. Parser singleton is
|
|
37
|
+
* shared across calls. */
|
|
38
|
+
function extractFileStructure(content, cachedTree) {
|
|
39
|
+
const tree = cachedTree ?? getCsharpParser().parse(content);
|
|
40
|
+
const namespaces = [];
|
|
41
|
+
const usingStaticPaths = [];
|
|
42
|
+
const visit = (node) => {
|
|
43
|
+
if (node.type === 'namespace_declaration' ||
|
|
44
|
+
node.type === 'file_scoped_namespace_declaration') {
|
|
45
|
+
const nameNode = node.childForFieldName('name');
|
|
46
|
+
if (nameNode !== null)
|
|
47
|
+
namespaces.push(nameNode.text);
|
|
48
|
+
}
|
|
49
|
+
else if (node.type === 'using_directive') {
|
|
50
|
+
// Inspect the directive's own text for the `static` keyword
|
|
51
|
+
// (tree-sitter-c-sharp does not expose it as a named child).
|
|
52
|
+
// This is a single-node-scoped text inspection, not a whole-file
|
|
53
|
+
// regex, so it stays well within AST semantics.
|
|
54
|
+
if (/^\s*(?:global\s+)?using\s+static\s/.test(node.text)) {
|
|
55
|
+
// Path lives on the `name:` field when the using-directive is
|
|
56
|
+
// aliased (`using static A = X.Y.Z;`); otherwise it's the
|
|
57
|
+
// first named child.
|
|
58
|
+
const aliasField = node.childForFieldName('name');
|
|
59
|
+
let pathNode = null;
|
|
60
|
+
if (aliasField !== null) {
|
|
61
|
+
for (const c of node.namedChildren) {
|
|
62
|
+
if (c !== null && c.startIndex !== aliasField.startIndex) {
|
|
63
|
+
pathNode = c;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
pathNode = node.namedChildren[0] ?? null;
|
|
70
|
+
}
|
|
71
|
+
if (pathNode !== null)
|
|
72
|
+
usingStaticPaths.push(pathNode.text);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const child of node.namedChildren) {
|
|
76
|
+
if (child !== null)
|
|
77
|
+
visit(child);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
visit(tree.rootNode);
|
|
81
|
+
return { namespaces, usingStaticPaths };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Mutate `indexes.bindings` in-place, adding cross-file sibling class
|
|
85
|
+
* defs to each Namespace scope. Class-like defs (Class / Interface /
|
|
86
|
+
* Struct / Record / Enum) are visible cross-file; method / field
|
|
87
|
+
* members are not.
|
|
88
|
+
*/
|
|
89
|
+
export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
|
|
90
|
+
// Build a structural view (namespaces + using-static paths) per
|
|
91
|
+
// file once up-front. Reuses the orchestrator's `treeCache` so
|
|
92
|
+
// files already parsed by `extractParsedFile` don't get re-parsed
|
|
93
|
+
// here — single-source-of-truth for the AST.
|
|
94
|
+
const structureByFile = new Map();
|
|
95
|
+
for (const parsed of parsedFiles) {
|
|
96
|
+
const content = inputs.fileContents.get(parsed.filePath);
|
|
97
|
+
if (content === undefined)
|
|
98
|
+
continue;
|
|
99
|
+
const cachedTree = inputs.treeCache?.get(parsed.filePath);
|
|
100
|
+
structureByFile.set(parsed.filePath, extractFileStructure(content, cachedTree));
|
|
101
|
+
}
|
|
102
|
+
const buckets = new Map();
|
|
103
|
+
const getBucket = (name) => {
|
|
104
|
+
let b = buckets.get(name);
|
|
105
|
+
if (b === undefined) {
|
|
106
|
+
b = { scopes: [], classDefs: [] };
|
|
107
|
+
buckets.set(name, b);
|
|
108
|
+
}
|
|
109
|
+
return b;
|
|
110
|
+
};
|
|
111
|
+
for (const parsed of parsedFiles) {
|
|
112
|
+
const struct = structureByFile.get(parsed.filePath);
|
|
113
|
+
if (struct === undefined)
|
|
114
|
+
continue;
|
|
115
|
+
// Declared namespace names, source order (AST walk visits children
|
|
116
|
+
// left-to-right, matching the scope-extractor's ordering).
|
|
117
|
+
const names = struct.namespaces.length > 0 ? [...struct.namespaces] : [''];
|
|
118
|
+
const namespaceScopes = parsed.scopes.filter((s) => s.kind === 'Namespace');
|
|
119
|
+
// With file-scoped namespaces (`namespace X;`), the Namespace
|
|
120
|
+
// scope's range covers only the declaration line, not the rest of
|
|
121
|
+
// the file — so classes below it land under the Module scope, not
|
|
122
|
+
// the Namespace scope. Group top-level classes by "any class whose
|
|
123
|
+
// parent scope is Module or Namespace" and attribute them to the
|
|
124
|
+
// first declared namespace in the file. Multiple-namespace files
|
|
125
|
+
// are rare enough that first-wins is the right first pass; fix
|
|
126
|
+
// when the parity suite surfaces a case.
|
|
127
|
+
const moduleScope = parsed.scopes.find((s) => s.kind === 'Module');
|
|
128
|
+
const topLevelParentIds = new Set();
|
|
129
|
+
if (moduleScope !== undefined)
|
|
130
|
+
topLevelParentIds.add(moduleScope.id);
|
|
131
|
+
for (const ns of namespaceScopes)
|
|
132
|
+
topLevelParentIds.add(ns.id);
|
|
133
|
+
// Attribute all top-level classes to the first-declared namespace
|
|
134
|
+
// in this file. Multiple-namespace files are rare and can be
|
|
135
|
+
// addressed if the parity suite surfaces a case. Inject into BOTH
|
|
136
|
+
// the Module and the Namespace scopes — the Module scope is on
|
|
137
|
+
// the ancestor chain of every function body (the Namespace scope
|
|
138
|
+
// is not, because file-scoped `namespace X;` has a 1-line range).
|
|
139
|
+
const firstName = names[0];
|
|
140
|
+
const bucket = getBucket(firstName);
|
|
141
|
+
if (moduleScope !== undefined) {
|
|
142
|
+
bucket.scopes.push({
|
|
143
|
+
filePath: parsed.filePath,
|
|
144
|
+
scopeId: moduleScope.id,
|
|
145
|
+
scope: moduleScope,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
for (const ns of namespaceScopes) {
|
|
149
|
+
bucket.scopes.push({ filePath: parsed.filePath, scopeId: ns.id, scope: ns });
|
|
150
|
+
}
|
|
151
|
+
for (const s of parsed.scopes) {
|
|
152
|
+
if (s.kind !== 'Class')
|
|
153
|
+
continue;
|
|
154
|
+
if (s.parent === null || !topLevelParentIds.has(s.parent))
|
|
155
|
+
continue;
|
|
156
|
+
for (const def of s.ownedDefs) {
|
|
157
|
+
if (isTypeDef(def)) {
|
|
158
|
+
bucket.classDefs.push(def);
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Inject cross-file siblings into each namespace scope's finalized
|
|
165
|
+
// bindings. `indexes.bindings` is typed `ReadonlyMap<ScopeId, ...>`
|
|
166
|
+
// but is a plain Map at runtime; mutating here is the established
|
|
167
|
+
// pattern (see `propagateImportedReturnTypes` which does the same
|
|
168
|
+
// for module-scope typeBindings).
|
|
169
|
+
const finalized = indexes.bindings;
|
|
170
|
+
// Cross-namespace type-binding propagation: for each file, mirror
|
|
171
|
+
// method return-type bindings from same-namespace sibling files and
|
|
172
|
+
// from files in namespaces the importer `using`s, into the
|
|
173
|
+
// importer's Module scope typeBindings. This enables
|
|
174
|
+
// chain-follow from `var u = svc.GetUser()` → `GetUser → User`
|
|
175
|
+
// even across files — without it the chain stalls at `GetUser`
|
|
176
|
+
// because the return binding lives in the defining file's Module
|
|
177
|
+
// scope, which isn't an ancestor of the importer's scope chain.
|
|
178
|
+
for (const parsed of parsedFiles) {
|
|
179
|
+
const moduleScope = parsed.scopes.find((s) => s.kind === 'Module');
|
|
180
|
+
if (moduleScope === undefined)
|
|
181
|
+
continue;
|
|
182
|
+
const moduleTypeBindings = moduleScope.typeBindings;
|
|
183
|
+
// Accessible namespaces = this file's own namespaces + every
|
|
184
|
+
// `using namespace X;` target. Source of truth is the cached AST
|
|
185
|
+
// structure captured above.
|
|
186
|
+
const accessibleNamespaces = new Set();
|
|
187
|
+
const struct = structureByFile.get(parsed.filePath);
|
|
188
|
+
if (struct !== undefined) {
|
|
189
|
+
for (const n of struct.namespaces)
|
|
190
|
+
accessibleNamespaces.add(n);
|
|
191
|
+
}
|
|
192
|
+
if (accessibleNamespaces.size === 0)
|
|
193
|
+
accessibleNamespaces.add('');
|
|
194
|
+
for (const imp of parsed.parsedImports) {
|
|
195
|
+
if (imp.kind === 'namespace' && imp.targetRaw !== null) {
|
|
196
|
+
accessibleNamespaces.add(imp.targetRaw);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// For each accessible namespace, also walk up the dotted path —
|
|
200
|
+
// `using static X.Y.Z;` targets a type, so the real namespace is
|
|
201
|
+
// `X.Y`. Both parse into `accessibleNamespaces` as-is; we probe
|
|
202
|
+
// the bucket map with every prefix.
|
|
203
|
+
const expandedNamespaces = new Set(accessibleNamespaces);
|
|
204
|
+
for (const ns of accessibleNamespaces) {
|
|
205
|
+
const segments = ns.split('.');
|
|
206
|
+
for (let i = segments.length - 1; i > 0; i--) {
|
|
207
|
+
expandedNamespaces.add(segments.slice(0, i).join('.'));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
for (const nsName of expandedNamespaces) {
|
|
211
|
+
const bucket = buckets.get(nsName);
|
|
212
|
+
if (bucket === undefined)
|
|
213
|
+
continue;
|
|
214
|
+
for (const scopeInfo of bucket.scopes) {
|
|
215
|
+
if (scopeInfo.filePath === parsed.filePath)
|
|
216
|
+
continue;
|
|
217
|
+
if (scopeInfo.scope.kind !== 'Module')
|
|
218
|
+
continue;
|
|
219
|
+
for (const [boundName, typeRef] of scopeInfo.scope.typeBindings) {
|
|
220
|
+
if (moduleTypeBindings.has(boundName))
|
|
221
|
+
continue;
|
|
222
|
+
moduleTypeBindings.set(boundName, typeRef);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// `using static X.Y.Z;` — expose every public static method of
|
|
228
|
+
// class Z as a free-callable binding in the importer's module
|
|
229
|
+
// scope, so `Record(...)` (without `Logger.` qualifier) resolves
|
|
230
|
+
// to `Logger.Record`. AST walk above captured these (including
|
|
231
|
+
// `global using static` and aliased forms).
|
|
232
|
+
for (const parsed of parsedFiles) {
|
|
233
|
+
const struct = structureByFile.get(parsed.filePath);
|
|
234
|
+
if (struct === undefined)
|
|
235
|
+
continue;
|
|
236
|
+
const moduleScope = parsed.scopes.find((s) => s.kind === 'Module');
|
|
237
|
+
if (moduleScope === undefined)
|
|
238
|
+
continue;
|
|
239
|
+
for (const fullPath of struct.usingStaticPaths) {
|
|
240
|
+
const lastDot = fullPath.lastIndexOf('.');
|
|
241
|
+
if (lastDot === -1)
|
|
242
|
+
continue;
|
|
243
|
+
const className = fullPath.slice(lastDot + 1);
|
|
244
|
+
const enclosingNs = fullPath.slice(0, lastDot);
|
|
245
|
+
// Find the target class in the named namespace bucket.
|
|
246
|
+
const bucket = buckets.get(enclosingNs);
|
|
247
|
+
if (bucket === undefined)
|
|
248
|
+
continue;
|
|
249
|
+
const targetDef = bucket.classDefs.find((d) => {
|
|
250
|
+
const q = d.qualifiedName ?? '';
|
|
251
|
+
const simple = q.includes('.') ? q.slice(q.lastIndexOf('.') + 1) : q;
|
|
252
|
+
return simple === className;
|
|
253
|
+
});
|
|
254
|
+
if (targetDef === undefined)
|
|
255
|
+
continue;
|
|
256
|
+
// Inject the class's member methods into the importer's module
|
|
257
|
+
// scope. `memberByOwner` wasn't built yet here, so we walk the
|
|
258
|
+
// file's localDefs to find members with `ownerId === targetDef.nodeId`.
|
|
259
|
+
const targetFile = parsedFiles.find((p) => p.filePath === targetDef.filePath);
|
|
260
|
+
if (targetFile === undefined)
|
|
261
|
+
continue;
|
|
262
|
+
for (const memberDef of targetFile.localDefs) {
|
|
263
|
+
if (memberDef.ownerId !== targetDef.nodeId)
|
|
264
|
+
continue;
|
|
265
|
+
if (memberDef.type !== 'Method' && memberDef.type !== 'Function')
|
|
266
|
+
continue;
|
|
267
|
+
const mq = memberDef.qualifiedName ?? '';
|
|
268
|
+
const simpleName = mq.includes('.') ? mq.slice(mq.lastIndexOf('.') + 1) : mq;
|
|
269
|
+
if (simpleName === '')
|
|
270
|
+
continue;
|
|
271
|
+
// Add to `indexes.bindings[moduleScope]` so
|
|
272
|
+
// `findCallableBindingInScope` picks it up.
|
|
273
|
+
let scopeBindings = finalized.get(moduleScope.id);
|
|
274
|
+
if (scopeBindings === undefined) {
|
|
275
|
+
scopeBindings = new Map();
|
|
276
|
+
finalized.set(moduleScope.id, scopeBindings);
|
|
277
|
+
}
|
|
278
|
+
const existing = scopeBindings.get(simpleName) ?? [];
|
|
279
|
+
if (existing.some((b) => b.def.nodeId === memberDef.nodeId))
|
|
280
|
+
continue;
|
|
281
|
+
existing.push({ def: memberDef, origin: 'import' });
|
|
282
|
+
scopeBindings.set(simpleName, existing);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Cross-namespace imports: for each file's `using X;` directive,
|
|
287
|
+
// if `X` matches a known namespace bucket, inject that bucket's
|
|
288
|
+
// classes into the importer's module scope. This is what makes
|
|
289
|
+
// `new User()` in `namespace App;` resolve to `User` declared in
|
|
290
|
+
// a sibling file with `namespace Models;` when the importer says
|
|
291
|
+
// `using Models;`. Legacy uses csproj directory↔namespace mapping;
|
|
292
|
+
// the scope-resolver layer uses the declared namespace directly.
|
|
293
|
+
for (const parsed of parsedFiles) {
|
|
294
|
+
const moduleScope = parsed.scopes.find((s) => s.kind === 'Module');
|
|
295
|
+
if (moduleScope === undefined)
|
|
296
|
+
continue;
|
|
297
|
+
for (const imp of parsed.parsedImports) {
|
|
298
|
+
if (imp.kind !== 'namespace')
|
|
299
|
+
continue;
|
|
300
|
+
const targetNs = imp.targetRaw;
|
|
301
|
+
if (targetNs === null || targetNs === '')
|
|
302
|
+
continue;
|
|
303
|
+
const bucket = buckets.get(targetNs);
|
|
304
|
+
if (bucket === undefined)
|
|
305
|
+
continue;
|
|
306
|
+
for (const def of bucket.classDefs) {
|
|
307
|
+
if (def.filePath === parsed.filePath)
|
|
308
|
+
continue;
|
|
309
|
+
const q = def.qualifiedName ?? '';
|
|
310
|
+
const simpleName = q.includes('.') ? q.slice(q.lastIndexOf('.') + 1) : q;
|
|
311
|
+
if (simpleName === '')
|
|
312
|
+
continue;
|
|
313
|
+
let scopeBindings = finalized.get(moduleScope.id);
|
|
314
|
+
if (scopeBindings === undefined) {
|
|
315
|
+
scopeBindings = new Map();
|
|
316
|
+
finalized.set(moduleScope.id, scopeBindings);
|
|
317
|
+
}
|
|
318
|
+
const existing = scopeBindings.get(simpleName) ?? [];
|
|
319
|
+
if (existing.some((b) => b.def.nodeId === def.nodeId))
|
|
320
|
+
continue;
|
|
321
|
+
existing.push({ def, origin: 'namespace' });
|
|
322
|
+
scopeBindings.set(simpleName, existing);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
for (const [, bucket] of buckets) {
|
|
327
|
+
// De-dup by (nodeId, filePath) across multiple declarations (e.g.
|
|
328
|
+
// partial classes declaring the same name in two files — we take
|
|
329
|
+
// both and leave de-dup to downstream consumers of bindings).
|
|
330
|
+
const defsByName = new Map();
|
|
331
|
+
for (const def of bucket.classDefs) {
|
|
332
|
+
// Simple name = last segment of qualifiedName (e.g. `App.User` → `User`).
|
|
333
|
+
const q = def.qualifiedName ?? '';
|
|
334
|
+
const key = q.includes('.') ? q.slice(q.lastIndexOf('.') + 1) : q;
|
|
335
|
+
if (key === '')
|
|
336
|
+
continue;
|
|
337
|
+
const arr = defsByName.get(key) ?? [];
|
|
338
|
+
arr.push(def);
|
|
339
|
+
defsByName.set(key, arr);
|
|
340
|
+
}
|
|
341
|
+
for (const { scopeId, filePath } of bucket.scopes) {
|
|
342
|
+
let scopeBindings = finalized.get(scopeId);
|
|
343
|
+
if (scopeBindings === undefined) {
|
|
344
|
+
scopeBindings = new Map();
|
|
345
|
+
finalized.set(scopeId, scopeBindings);
|
|
346
|
+
}
|
|
347
|
+
for (const [name, defs] of defsByName) {
|
|
348
|
+
// Skip names already present locally — `origin: 'local'` in
|
|
349
|
+
// scope.bindings would naturally shadow the cross-file
|
|
350
|
+
// namespace entry, but we also keep this index lean.
|
|
351
|
+
const local = bucket.scopes.find((s) => s.filePath === filePath)?.scope.bindings.get(name);
|
|
352
|
+
if (local !== undefined && local.some((b) => b.origin === 'local'))
|
|
353
|
+
continue;
|
|
354
|
+
const existing = scopeBindings.get(name) ?? [];
|
|
355
|
+
for (const def of defs) {
|
|
356
|
+
if (def.filePath === filePath)
|
|
357
|
+
continue; // don't self-reference
|
|
358
|
+
if (existing.some((b) => b.def.nodeId === def.nodeId))
|
|
359
|
+
continue;
|
|
360
|
+
existing.push({ def, origin: 'namespace' });
|
|
361
|
+
}
|
|
362
|
+
if (existing.length > 0)
|
|
363
|
+
scopeBindings.set(name, existing);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function isTypeDef(def) {
|
|
369
|
+
return (def.type === 'Class' ||
|
|
370
|
+
def.type === 'Interface' ||
|
|
371
|
+
def.type === 'Struct' ||
|
|
372
|
+
def.type === 'Record' ||
|
|
373
|
+
def.type === 'Enum');
|
|
374
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter query for C# scope captures (RFC §5.1).
|
|
3
|
+
*
|
|
4
|
+
* Captures the structural skeleton the generic scope-resolution
|
|
5
|
+
* pipeline consumes: scopes (module/namespace/class/function),
|
|
6
|
+
* declarations (class-likes, method-likes, properties, variables,
|
|
7
|
+
* local functions), imports (using directives), type bindings
|
|
8
|
+
* (parameter annotations, variable annotations, constructor
|
|
9
|
+
* inference), and references (call sites, member writes).
|
|
10
|
+
*
|
|
11
|
+
* C# specifics that shape this query:
|
|
12
|
+
*
|
|
13
|
+
* - Both block-scoped (`namespace X { }`) and file-scoped
|
|
14
|
+
* (`namespace X;`) namespaces. tree-sitter-c-sharp emits them
|
|
15
|
+
* under distinct node types (`namespace_declaration` vs
|
|
16
|
+
* `file_scoped_namespace_declaration`); both map to
|
|
17
|
+
* `@scope.namespace` since the scope semantics are identical.
|
|
18
|
+
* - `partial class X` splits a Class def across files. Each file
|
|
19
|
+
* emits its own `@declaration.class`; cross-file resolution is
|
|
20
|
+
* handled at the graph-bridge layer via the qualified-name key.
|
|
21
|
+
* - `using X = Y;` aliases and `using static X;` are interpreted in
|
|
22
|
+
* `interpret.ts` via the `@import.*` captures. All three using
|
|
23
|
+
* flavors share the same anchor (`@import.statement`).
|
|
24
|
+
* - Explicit interface implementations (`void IFoo.Bar() { }`)
|
|
25
|
+
* expose the qualified name via the existing `@declaration.name`
|
|
26
|
+
* — the extractor's `csharpMethodConfig.extractQualifiedName`
|
|
27
|
+
* picks up the explicit qualifier from the method declaration
|
|
28
|
+
* node.
|
|
29
|
+
*
|
|
30
|
+
* Exposes lazy `Parser` and `Query` singletons so callers don't pay
|
|
31
|
+
* tree-sitter init cost per file.
|
|
32
|
+
*/
|
|
33
|
+
import Parser from 'tree-sitter';
|
|
34
|
+
export declare function getCsharpParser(): Parser;
|
|
35
|
+
export declare function getCsharpScopeQuery(): Parser.Query;
|