mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-24 05:43:42 +00:00
refactor(scope-resolution): export canParentScope from gitnexus-shared
Addresses #1087 review (medium): the helper was previously duplicated byte-for-byte in `scope-extractor.ts` and `scope-tree.ts`. Per DoD "single source of truth in shared", the contract piece belongs in gitnexus-shared (Ring 2 SHARED #912) and the consuming layer should import it. Eliminates the silent-drift surface where a future edit to one copy would produce extractor/validator disagreement on what a well-formed parent edge looks like. Changes: - gitnexus-shared/src/scope-resolution/scope-tree.ts: add `export` to `canParentScope`. - gitnexus-shared/src/index.ts: re-export `canParentScope`. - gitnexus/src/core/ingestion/scope-extractor.ts: remove the local `canParentScope` definition (and its now-unused local copy of `rangeStrictlyContains`), import from `gitnexus-shared`. The local `rangesEqual` stays — it's still used in capture-anchor logic at two unrelated sites. Validation (per DoD §4.4 — both CLI and web consumers verified): - npx tsc --noEmit clean in gitnexus/ and gitnexus-shared/ - cd gitnexus-web && npx tsc -b --noEmit clean - gitnexus-shared `npm run build` clean - Targeted: vitest run test/unit/scope-resolution test/integration/resolvers → 2522 passed / 0 failed / 77 skipped (54 files) - Full suite: vitest run → 7238 passed / 1 failed / 97 skipped. The single failure is `test/unit/ignore-service.test.ts > warns on EACCES but does not throw`, which cannot run when uid=0 (root bypasses POSIX permission checks). Pre-existing on this branch before the refactor; unrelated to scope-resolution.
This commit is contained in:
parent
fbdf2ede54
commit
d39482dffe
3 changed files with 7 additions and 53 deletions
|
|
@ -134,7 +134,11 @@ export type {
|
|||
// Scope tree spine + position lookup (RFC §2.2 + §3.1; Ring 2 SHARED #912)
|
||||
export { makeScopeId, clearScopeIdInternPool } from './scope-resolution/scope-id.js';
|
||||
export type { ScopeIdInput } from './scope-resolution/scope-id.js';
|
||||
export { buildScopeTree, ScopeTreeInvariantError } from './scope-resolution/scope-tree.js';
|
||||
export {
|
||||
buildScopeTree,
|
||||
canParentScope,
|
||||
ScopeTreeInvariantError,
|
||||
} from './scope-resolution/scope-tree.js';
|
||||
export type { ScopeTree } from './scope-resolution/scope-tree.js';
|
||||
export { buildPositionIndex } from './scope-resolution/position-index.js';
|
||||
export type { PositionIndex } from './scope-resolution/position-index.js';
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ function rangesEqual(a: Range, b: Range): boolean {
|
|||
* helper in `scope-extractor.ts` so `pass1BuildScopes` and the validator
|
||||
* agree on what a well-formed parent edge looks like.
|
||||
*/
|
||||
function canParentScope(
|
||||
export function canParentScope(
|
||||
outer: Range,
|
||||
inner: Range,
|
||||
outerKind: Scope['kind'],
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ import type {
|
|||
SymbolDefinition,
|
||||
TypeRef,
|
||||
} from 'gitnexus-shared';
|
||||
import { buildPositionIndex, buildScopeTree, makeScopeId } from 'gitnexus-shared';
|
||||
import { buildPositionIndex, buildScopeTree, canParentScope, makeScopeId } from 'gitnexus-shared';
|
||||
import type { LanguageProvider } from './language-provider.js';
|
||||
|
||||
// ─── Narrow hook surface the extractor actually uses ───────────────────────
|
||||
|
|
@ -924,56 +924,6 @@ function rangesEqual(a: Range, b: Range): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
function rangeStrictlyContains(outer: Range, inner: Range): boolean {
|
||||
if (
|
||||
outer.startLine === inner.startLine &&
|
||||
outer.startCol === inner.startCol &&
|
||||
outer.endLine === inner.endLine &&
|
||||
outer.endCol === inner.endCol
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const startsBefore =
|
||||
outer.startLine < inner.startLine ||
|
||||
(outer.startLine === inner.startLine && outer.startCol <= inner.startCol);
|
||||
const endsAfter =
|
||||
outer.endLine > inner.endLine ||
|
||||
(outer.endLine === inner.endLine && outer.endCol >= inner.endCol);
|
||||
return startsBefore && endsAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether `outer` (kind `outerKind`) is a valid parent for `inner` (kind
|
||||
* `innerKind`) by tree-sitter range geometry.
|
||||
*
|
||||
* Strict containment is the general rule. The single carve-out is the
|
||||
* `Module`/non-Module pair whose ranges are exactly equal — this happens
|
||||
* naturally when tree-sitter reports identical byte spans for the
|
||||
* `compilation_unit` and the file's single top-level scope (e.g. a C# file
|
||||
* consisting of nothing but `namespace X { ... }` with no leading or
|
||||
* trailing trivia outside the namespace's `{}` body). The `Module`
|
||||
* is the universal outer of any file-level scope by language semantics, so
|
||||
* coincident ranges should not break the parent chain. The `rangesEqual`
|
||||
* Module carve-out is direction-asymmetric: only Module-as-outer parents
|
||||
* a same-range non-Module, never the reverse, so the relationship stays
|
||||
* acyclic and `buildScopeTree`'s parent-must-share-filePath / no-cycle
|
||||
* invariants continue to hold.
|
||||
*
|
||||
* `rangeStrictlyContains` keeps its strict semantics so position-index
|
||||
* lookups, hook-side range comparisons, and other call sites are
|
||||
* unchanged.
|
||||
*/
|
||||
function canParentScope(
|
||||
outer: Range,
|
||||
inner: Range,
|
||||
outerKind: ScopeKind,
|
||||
innerKind: ScopeKind,
|
||||
): boolean {
|
||||
if (rangeStrictlyContains(outer, inner)) return true;
|
||||
if (outerKind === 'Module' && innerKind !== 'Module' && rangesEqual(outer, inner)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture names that are never anchors — they are sub-tags nested inside a
|
||||
* larger anchor (e.g., the receiver expression inside a `@reference.call`
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue