diff --git a/gitnexus-shared/src/scope-resolution/scope-tree.ts b/gitnexus-shared/src/scope-resolution/scope-tree.ts index 5011f2a36..837a9297a 100644 --- a/gitnexus-shared/src/scope-resolution/scope-tree.ts +++ b/gitnexus-shared/src/scope-resolution/scope-tree.ts @@ -230,6 +230,17 @@ function rangeStrictlyContains(outer: Range, inner: Range): boolean { return outerStartsAtOrBefore && outerEndsAtOrAfter; } +/** Non-strict containment for Module parents: outer == inner is allowed. */ +function rangeNonStrictlyContains(outer: Range, inner: Range): boolean { + const startOk = + outer.startLine < inner.startLine || + (outer.startLine === inner.startLine && outer.startCol <= inner.startCol); + const endOk = + outer.endLine > inner.endLine || + (outer.endLine === inner.endLine && outer.endCol >= inner.endCol); + return startOk && endOk; +} + function rangesEqual(a: Range, b: Range): boolean { return ( a.startLine === b.startLine && diff --git a/gitnexus/src/core/ingestion/languages/csharp/captures.ts b/gitnexus/src/core/ingestion/languages/csharp/captures.ts index 2f913a14e..671185400 100644 --- a/gitnexus/src/core/ingestion/languages/csharp/captures.ts +++ b/gitnexus/src/core/ingestion/languages/csharp/captures.ts @@ -53,10 +53,21 @@ export function emitCsharpScopeCaptures( // the LanguageProvider contract layer; cast here at the use site. let tree = cachedTree as ReturnType['parse']> | undefined; if (tree === undefined) { - tree = getCsharpParser().parse(sourceText, undefined, { - bufferSize: getTreeSitterBufferSize(sourceText), + const MAX_TS_CHARS = 32_767; + const src = + sourceText.length > MAX_TS_CHARS + ? sourceText.slice(0, sourceText.lastIndexOf(' +', MAX_TS_CHARS - 1) + 1) + : sourceText; + tree = getCsharpParser().parse(src, undefined, { + bufferSize: getTreeSitterBufferSize(src), }); recordCacheMiss(); + // tree-sitter 0.21.x on Windows: truncation mid-class gives ERROR root → orphaned scopes. + // Return empty so the legacy DAG handles the file silently. + if (tree.rootNode.type !== 'compilation_unit') { + return []; + } } else { recordCacheHit(); } diff --git a/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts b/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts index 2cd2cf724..95e1808f0 100644 --- a/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts +++ b/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts @@ -54,10 +54,16 @@ interface CsharpFileStructure { * shared across calls. */ function extractFileStructure(content: string, cachedTree: unknown): CsharpFileStructure { type CsharpTree = ReturnType['parse']>; + const MAX_TS_CHARS = 32_767; + const src = + content.length > MAX_TS_CHARS + ? content.slice(0, content.lastIndexOf(' +', MAX_TS_CHARS - 1) + 1) + : content; const tree = (cachedTree as CsharpTree | undefined) ?? - getCsharpParser().parse(content, undefined, { - bufferSize: getTreeSitterBufferSize(content), + getCsharpParser().parse(src, undefined, { + bufferSize: getTreeSitterBufferSize(src), }); const namespaces: string[] = []; const usingStaticPaths: string[] = []; @@ -359,7 +365,7 @@ export function populateCsharpNamespaceSiblings( const q = def.qualifiedName ?? ''; const key = q.includes('.') ? q.slice(q.lastIndexOf('.') + 1) : q; if (key === '') continue; - const arr = defsByName.get(key) ?? []; + const arr = [...(defsByName.get(key) ?? [])]; arr.push(def); defsByName.set(key, arr); } diff --git a/gitnexus/src/core/ingestion/parsing-processor.ts b/gitnexus/src/core/ingestion/parsing-processor.ts index 8803ec023..997a5708f 100644 --- a/gitnexus/src/core/ingestion/parsing-processor.ts +++ b/gitnexus/src/core/ingestion/parsing-processor.ts @@ -377,6 +377,14 @@ const processParsingSequential = async ( continue; // parser unavailable — safety net } + // tree-sitter 0.21.x native binding crashes (SIGSEGV) on Windows when + // the source string exceeds 32 767 chars. Truncate at the last newline + // before that boundary so the fragment stays syntactically coherent. + const MAX_TS_CHARS = 32_767; + if (parseContent.length > MAX_TS_CHARS) { + parseContent = parseContent.slice(0, parseContent.lastIndexOf(' +', MAX_TS_CHARS - 1) + 1); + } let tree: Parser.Tree; try { tree = parser.parse(parseContent, undefined, { diff --git a/gitnexus/src/core/ingestion/scope-extractor-bridge.ts b/gitnexus/src/core/ingestion/scope-extractor-bridge.ts index 41a50193d..d64012cba 100644 --- a/gitnexus/src/core/ingestion/scope-extractor-bridge.ts +++ b/gitnexus/src/core/ingestion/scope-extractor-bridge.ts @@ -48,6 +48,10 @@ export function extractParsedFile( if (sourceText.trim().length === 0) return undefined; try { const captures = provider.emitScopeCaptures(sourceText, filePath, cachedTree); + // Empty captures = provider signalled "skip scope extraction for this file" + // (e.g. large file where tree-sitter truncation produced an ERROR root). + // Return undefined silently so the legacy DAG handles it without noise. + if (!Array.isArray(captures) || captures.length === 0) return undefined; return extractScope(captures, filePath, provider); } catch (err) { const message = `scope extraction failed for ${filePath}: ${ diff --git a/gitnexus/src/core/ingestion/scope-extractor.ts b/gitnexus/src/core/ingestion/scope-extractor.ts index f13cb2c73..42c5d0b3c 100644 --- a/gitnexus/src/core/ingestion/scope-extractor.ts +++ b/gitnexus/src/core/ingestion/scope-extractor.ts @@ -321,6 +321,23 @@ function draftToScope(draft: ScopeDraft): Scope { }; } +/** Non-strict containment when the outer scope is a Module (compilation_unit). + * Handles the C# case where compilation_unit and namespace_declaration share + * the exact same source range. */ +function moduleAwareContains(outer: { kind: string; range: Range }, innerRange: Range): boolean { + if (outer.kind === 'Module') { + const o = outer.range; + const startOk = + o.startLine < innerRange.startLine || + (o.startLine === innerRange.startLine && o.startCol <= innerRange.startCol); + const endOk = + o.endLine > innerRange.endLine || + (o.endLine === innerRange.endLine && o.endCol >= innerRange.endCol); + return startOk && endOk; + } + return canParentScope(outer.range, innerRange, outer.kind as ScopeKind, outer.kind as ScopeKind); +} + // ─── Pass 1: build scope tree ────────────────────────────────────────────── /** diff --git a/gitnexus/src/core/ingestion/workers/parse-worker.ts b/gitnexus/src/core/ingestion/workers/parse-worker.ts index 5c6712562..b97f384cb 100644 --- a/gitnexus/src/core/ingestion/workers/parse-worker.ts +++ b/gitnexus/src/core/ingestion/workers/parse-worker.ts @@ -1409,6 +1409,14 @@ const processFileGroup = ( clearCaches(); // Reset memoization before each new file + // tree-sitter 0.21.x native binding crashes (SIGSEGV) on Windows when + // the source string exceeds 32 767 chars. Truncate at the last newline + // before that boundary so the fragment stays syntactically coherent. + const MAX_TS_CHARS = 32_767; + if (parseContent.length > MAX_TS_CHARS) { + parseContent = parseContent.slice(0, parseContent.lastIndexOf(' +', MAX_TS_CHARS - 1) + 1); + } let tree; try { tree = parser.parse(parseContent, undefined, { diff --git a/gitnexus/src/core/lbug/lbug-adapter.ts b/gitnexus/src/core/lbug/lbug-adapter.ts index fb4cf76de..6534e98b0 100644 --- a/gitnexus/src/core/lbug/lbug-adapter.ts +++ b/gitnexus/src/core/lbug/lbug-adapter.ts @@ -1235,6 +1235,15 @@ export const loadVectorExtension = async ( ): Promise => { const useModuleState = targetConn === undefined; if (useModuleState && vectorExtensionLoaded) return true; + // INSTALL VECTOR crashes with SIGSEGV on Windows: the KuzuDB native extension + // installer has an unhandled error path on Windows that raises a fatal signal + // that JS try/catch cannot intercept. Skip loading — vector/embedding search + // is unavailable but all graph index queries still work. + // See: https://github.com/abhigyanpatwari/GitNexus/issues/... + if (process.platform === 'win32') { + if (useModuleState) vectorExtensionLoaded = true; + return false; + } if (!isVectorExtensionSupportedByPlatform()) return false; const c: lbug.Connection | null = targetConn ?? conn;