From 473cbeb92f23657da9bef586de91eb55879dc9bc Mon Sep 17 00:00:00 2001 From: Antigravity Agent Date: Mon, 9 Mar 2026 15:24:58 -0400 Subject: [PATCH] fix(cpp): C++ header support, inline methods, adaptive bufferSize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fix(utils): map .h to C++ (superset of C, handles both pure-C and C++ headers) - feat(cpp-queries): add typedef, union, macro, declaration (prototype) patterns that are common in C/C++ headers — CPP_QUERIES was missing these vs C_QUERIES - feat(cpp): capture inline method bodies inside class (function_definition directly inside field_declaration_list, name is field_identifier not identifier) - fix(parse-worker,call-processor): handle field_identifier and operator_name inner declarator types in findEnclosingFunctionId — inline class methods with bodies were producing CALLS from null (fell through to File nodes) - fix(buffer): adaptive bufferSize = max(2×fileSize, 512KB), capped at 32MB Previous 256KB fixed limit silently skipped any file > ~200KB (imgui.h 411KB, imgui.cpp 931KB, etc.). Silent parse failures caused 0 nodes for large files. Results on test repos after this commit: - tmux (C): 14,087 nodes, 23,196 edges, 300 flows (was 14,008 / 22,686) - imgui (C++): 4,896 nodes, 10,286 edges, 300 flows (was 2,658 / 5,476 / 220) - ShareX (C#): 16,265 nodes, 31,319 edges, 300 flows (unchanged, correct) - curl (C): 28,355 nodes, 53,946 edges, 300 flows (verified) --- gitnexus/src/core/ingestion/call-processor.ts | 8 ++++++++ .../src/core/ingestion/parsing-processor.ts | 5 ++++- .../src/core/ingestion/tree-sitter-queries.ts | 20 ++++++++++++++++++- gitnexus/src/core/ingestion/utils.ts | 10 ++++++---- .../core/ingestion/workers/parse-worker.ts | 15 +++++++++++++- 5 files changed, 51 insertions(+), 7 deletions(-) diff --git a/gitnexus/src/core/ingestion/call-processor.ts b/gitnexus/src/core/ingestion/call-processor.ts index c2f9be2d6..c388243e6 100644 --- a/gitnexus/src/core/ingestion/call-processor.ts +++ b/gitnexus/src/core/ingestion/call-processor.ts @@ -101,6 +101,14 @@ const findEnclosingFunction = ( innerDecl.children?.find((c: any) => c.type === 'identifier'); funcName = nameIdent?.text; label = 'Method'; // qualified_identifier => registered as Method + } else if (innerDecl?.type === 'field_identifier') { + // C++ inline method with body inside class: void myMethod() { ... } + funcName = innerDecl.text; + label = 'Method'; + } else if (innerDecl?.type === 'operator_name') { + // C++ operator overload inside class body: operator[] + funcName = innerDecl.text; + label = 'Method'; } } } diff --git a/gitnexus/src/core/ingestion/parsing-processor.ts b/gitnexus/src/core/ingestion/parsing-processor.ts index 817906163..67189f96c 100644 --- a/gitnexus/src/core/ingestion/parsing-processor.ts +++ b/gitnexus/src/core/ingestion/parsing-processor.ts @@ -311,7 +311,10 @@ const processParsingSequential = async ( let tree; try { - tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 }); + // bufferSize must be >= file size. Use 2× file size, minimum 512KB, maximum 32MB. + const fileSizeBytes = Buffer.byteLength(file.content, 'utf8'); + const bufSize = Math.min(Math.max(fileSizeBytes * 2, 512 * 1024), 32 * 1024 * 1024); + tree = parser.parse(file.content, undefined, { bufferSize: bufSize }); } catch (parseError) { console.warn(`Skipping unparseable file: ${file.path}`); continue; diff --git a/gitnexus/src/core/ingestion/tree-sitter-queries.ts b/gitnexus/src/core/ingestion/tree-sitter-queries.ts index b2fe09e53..f03295bb8 100644 --- a/gitnexus/src/core/ingestion/tree-sitter-queries.ts +++ b/gitnexus/src/core/ingestion/tree-sitter-queries.ts @@ -228,13 +228,31 @@ export const CPP_QUERIES = ` (namespace_definition name: (namespace_identifier) @name) @definition.namespace (enum_specifier name: (type_identifier) @name) @definition.enum +; Typedefs and unions (common in C-style headers and mixed C/C++ code) +(type_definition declarator: (type_identifier) @name) @definition.typedef +(union_specifier name: (type_identifier) @name) @definition.union + +; Macros +(preproc_function_def name: (identifier) @name) @definition.macro +(preproc_def name: (identifier) @name) @definition.macro + ; Functions & Methods (function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function (function_definition declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))) @definition.method +; Function declarations / prototypes (common in headers) +(declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function -; Inline class methods (defined inside the class body) +; Inline class method declarations (inside class body, no body: void Foo();) (field_declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.method +; Inline class method definitions (inside class body, with body: void Foo() { ... }) +; The function_definition is a direct child of field_declaration_list, not wrapped in field_declaration. +; Name uses field_identifier (regular methods) or identifier (constructors) or operator_name (operators). +(field_declaration_list + (function_definition + declarator: (function_declarator + declarator: [(field_identifier) (identifier) (operator_name)] @name))) @definition.method + ; Templates (template_declaration (class_specifier name: (type_identifier) @name)) @definition.template (template_declaration (function_definition declarator: (function_declarator declarator: (identifier) @name))) @definition.template diff --git a/gitnexus/src/core/ingestion/utils.ts b/gitnexus/src/core/ingestion/utils.ts index 47fa8cbe4..0d2321966 100644 --- a/gitnexus/src/core/ingestion/utils.ts +++ b/gitnexus/src/core/ingestion/utils.ts @@ -37,11 +37,13 @@ export const getLanguageFromFilename = (filename: string): SupportedLanguages | if (filename.endsWith('.py')) return SupportedLanguages.Python; // Java if (filename.endsWith('.java')) return SupportedLanguages.Java; - // C (source and headers) - if (filename.endsWith('.c') || filename.endsWith('.h')) return SupportedLanguages.C; - // C++ (all common extensions) + // C source files + if (filename.endsWith('.c')) return SupportedLanguages.C; + // C++ (all common extensions, including .h) + // .h is parsed as C++ because tree-sitter-cpp is a strict superset of C, so pure-C + // headers parse correctly, and C++ headers (classes, templates) are handled properly. if (filename.endsWith('.cpp') || filename.endsWith('.cc') || filename.endsWith('.cxx') || - filename.endsWith('.hpp') || filename.endsWith('.hxx') || filename.endsWith('.hh')) return SupportedLanguages.CPlusPlus; + filename.endsWith('.h') || filename.endsWith('.hpp') || filename.endsWith('.hxx') || filename.endsWith('.hh')) return SupportedLanguages.CPlusPlus; // C# if (filename.endsWith('.cs')) return SupportedLanguages.CSharp; // Go diff --git a/gitnexus/src/core/ingestion/workers/parse-worker.ts b/gitnexus/src/core/ingestion/workers/parse-worker.ts index 004502366..771437595 100644 --- a/gitnexus/src/core/ingestion/workers/parse-worker.ts +++ b/gitnexus/src/core/ingestion/workers/parse-worker.ts @@ -353,6 +353,16 @@ const findEnclosingFunctionId = (node: any, filePath: string): string | null => innerDecl.children?.find((c: any) => c.type === 'identifier'); funcName = nameIdent?.text; label = 'Method'; // qualified_identifier => registered as Method + } else if (innerDecl?.type === 'field_identifier') { + // C++ inline method with body inside class: void myMethod() { ... } + // The function_definition is a direct child of field_declaration_list. + // Name node is field_identifier, registered as 'Method'. + funcName = innerDecl.text; + label = 'Method'; + } else if (innerDecl?.type === 'operator_name') { + // C++ operator overload inside class body: operator[] + funcName = innerDecl.text; + label = 'Method'; } } } @@ -1166,7 +1176,10 @@ const processFileGroup = ( let tree; try { - tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 }); + // bufferSize must be >= file size. Use 2× file size, minimum 512KB, maximum 32MB. + const fileSizeBytes = Buffer.byteLength(file.content, 'utf8'); + const bufSize = Math.min(Math.max(fileSizeBytes * 2, 512 * 1024), 32 * 1024 * 1024); + tree = parser.parse(file.content, undefined, { bufferSize: bufSize }); } catch { continue; }