Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9570d78591 | ||
|
|
473cbeb92f | ||
|
|
355e4b36cf | ||
|
|
e5d3480fa3 |
@@ -75,10 +75,43 @@ const findEnclosingFunction = (
|
||||
current.type === 'async_function_declaration' ||
|
||||
current.type === 'generator_function_declaration' ||
|
||||
current.type === 'function_item') { // Rust function
|
||||
// Named function: function foo() {}
|
||||
// Try direct name field (JS/TS/Python/Rust)
|
||||
const nameNode = current.childForFieldName?.('name') ||
|
||||
current.children?.find((c: any) => c.type === 'identifier' || c.type === 'property_identifier');
|
||||
funcName = nameNode?.text;
|
||||
if (nameNode) {
|
||||
funcName = nameNode.text;
|
||||
// C++ template functions: function_definition inside template_declaration
|
||||
// are registered as 'Template' nodes (not 'Function'), so match that label.
|
||||
if (current.type === 'function_definition' && current.parent?.type === 'template_declaration') {
|
||||
label = 'Template';
|
||||
}
|
||||
} else {
|
||||
// C/C++: name nested inside declarator -> function_declarator -> identifier
|
||||
const declarator = current.childForFieldName?.('declarator');
|
||||
if (declarator) {
|
||||
const innerDecl = declarator.childForFieldName?.('declarator');
|
||||
if (innerDecl?.type === 'identifier') {
|
||||
funcName = innerDecl.text;
|
||||
// Template function with declarator-style name
|
||||
if (current.parent?.type === 'template_declaration') {
|
||||
label = 'Template';
|
||||
}
|
||||
} else if (innerDecl?.type === 'qualified_identifier') {
|
||||
const nameIdent = innerDecl.childForFieldName?.('name') ||
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (current.type === 'impl_item') {
|
||||
// Rust method inside impl block: wrapper around function_item or const_item
|
||||
// We need to look inside for the function_item
|
||||
@@ -325,7 +358,8 @@ const BUILT_IN_NAMES = new Set([
|
||||
'hasOwnProperty', 'toString', 'valueOf',
|
||||
// Python built-ins
|
||||
'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
|
||||
'open', 'read', 'write', 'close', 'append', 'extend', 'update',
|
||||
'append', 'extend', 'update',
|
||||
// NOTE: 'open', 'read', 'write', 'close' removed — these are real C POSIX syscalls
|
||||
'super', 'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr',
|
||||
'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs',
|
||||
// Kotlin stdlib (IMPORTANT: keep in sync with parse-worker.ts BUILT_IN_NAMES)
|
||||
|
||||
@@ -86,21 +86,60 @@ const ENTRY_POINT_PATTERNS: Record<string, RegExp[]> = {
|
||||
/^spawn/, // Async spawn
|
||||
],
|
||||
|
||||
// C - explicit main() boost (critical for C programs)
|
||||
// C - explicit main() boost plus common C entry point conventions
|
||||
'c': [
|
||||
/^main$/, // THE entry point
|
||||
/^init_/, // Initialization functions
|
||||
/^start_/, // Start functions
|
||||
/^run_/, // Run functions
|
||||
/^init_/, // init_server, init_client
|
||||
/_init$/, // module_init, server_init
|
||||
/^start_/, // start_server
|
||||
/_start$/, // thread_start
|
||||
/^run_/, // run_loop
|
||||
/_run$/, // event_run
|
||||
/^stop_/, // stop_server
|
||||
/_stop$/, // service_stop
|
||||
/^open_/, // open_connection
|
||||
/_open$/, // file_open
|
||||
/^close_/, // close_connection
|
||||
/_close$/, // socket_close
|
||||
/^create_/, // create_session
|
||||
/_create$/, // object_create
|
||||
/^destroy_/, // destroy_session
|
||||
/_destroy$/, // object_destroy
|
||||
/^handle_/, // handle_request
|
||||
/_handler$/, // signal_handler
|
||||
/_callback$/, // event_callback
|
||||
/^cmd_/, // tmux: cmd_new_window, cmd_attach_session
|
||||
/^server_/, // server_start, server_loop
|
||||
/^client_/, // client_connect
|
||||
/^session_/, // session_create
|
||||
/^window_/, // window_resize (tmux)
|
||||
/^key_/, // key_press
|
||||
/^input_/, // input_parse
|
||||
/^output_/, // output_write
|
||||
/^notify_/, // notify_client
|
||||
/^control_/, // control_start
|
||||
],
|
||||
|
||||
// C++ - same as C plus class patterns
|
||||
// C++ - same as C plus OOP/template patterns
|
||||
'cpp': [
|
||||
/^main$/, // THE entry point
|
||||
/^init_/,
|
||||
/_init$/,
|
||||
/^Create[A-Z]/, // Factory patterns
|
||||
/^create_/,
|
||||
/^Run$/, // Run methods
|
||||
/^run$/,
|
||||
/^Start$/, // Start methods
|
||||
/^start$/,
|
||||
/^handle_/,
|
||||
/_handler$/,
|
||||
/_callback$/,
|
||||
/^OnEvent/, // Event callbacks
|
||||
/^on_/,
|
||||
/::Run$/, // Class::Run
|
||||
/::Start$/, // Class::Start
|
||||
/::Init$/, // Class::Init
|
||||
/::Execute$/, // Class::Execute
|
||||
],
|
||||
|
||||
// Swift / iOS
|
||||
|
||||
@@ -133,14 +133,27 @@ export const isNodeExported = (node: any, name: string, language: string): boole
|
||||
return first === first.toUpperCase() && first !== first.toLowerCase();
|
||||
|
||||
// Rust: Check for 'pub' visibility modifier
|
||||
case 'rust':
|
||||
// In Rust AST, `visibility_modifier` is a SIBLING of the name node within the
|
||||
// declaration node (function_item, struct_item, etc.), not a parent of it.
|
||||
// Fix: walk up to the declaration node, then scan its direct children.
|
||||
case 'rust': {
|
||||
const RUST_DECL_TYPES = new Set([
|
||||
'function_item', 'struct_item', 'enum_item', 'trait_item', 'impl_item',
|
||||
'type_item', 'const_item', 'static_item', 'mod_item', 'use_declaration',
|
||||
'associated_type', 'function_signature_item',
|
||||
]);
|
||||
while (current) {
|
||||
if (current.type === 'visibility_modifier') {
|
||||
if (current.text?.includes('pub')) return true;
|
||||
if (RUST_DECL_TYPES.has(current.type)) {
|
||||
for (let i = 0; i < current.childCount; i++) {
|
||||
const child = current.child(i);
|
||||
if (child?.type === 'visibility_modifier' && child.text?.startsWith('pub')) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Kotlin: Default visibility is public (unlike Java)
|
||||
// visibility_modifier is inside modifiers, a sibling of the name node within the declaration
|
||||
@@ -159,11 +172,25 @@ export const isNodeExported = (node: any, name: string, language: string): boole
|
||||
// No visibility modifier = public (Kotlin default)
|
||||
return true;
|
||||
|
||||
// C/C++: No native export concept at language level
|
||||
// Entry points will be detected via name patterns (main, etc.)
|
||||
// C/C++: Functions without 'static' storage class have external linkage
|
||||
// by default, making them globally accessible (equivalent to exported).
|
||||
// Only functions explicitly marked 'static' are file-scoped (not exported).
|
||||
case 'c':
|
||||
case 'cpp':
|
||||
return false;
|
||||
case 'cpp': {
|
||||
// Walk up to the function_definition/declaration and check for 'static'
|
||||
let cur = node;
|
||||
while (cur) {
|
||||
if (cur.type === 'function_definition' || cur.type === 'declaration') {
|
||||
// Check text before the opening brace (or semicolon) for 'static'
|
||||
const declText: string = (cur.text || '').split('{')[0].split(';')[0];
|
||||
// 'static' as a storage class (not 'static_assert' etc.)
|
||||
if (/\bstatic\b/.test(declText)) return false;
|
||||
return true; // No 'static' = external linkage = exported
|
||||
}
|
||||
cur = cur.parent;
|
||||
}
|
||||
return true; // Top-level C/C++ functions default to external linkage
|
||||
}
|
||||
|
||||
// Swift: Check for 'public' or 'open' access modifiers
|
||||
case 'swift':
|
||||
@@ -297,7 +324,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;
|
||||
|
||||
@@ -228,9 +228,30 @@ 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 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
|
||||
@@ -282,9 +303,9 @@ export const CSHARP_QUERIES = `
|
||||
|
||||
; Heritage
|
||||
(class_declaration name: (identifier) @heritage.class
|
||||
(base_list (simple_base_type (identifier) @heritage.extends))) @heritage
|
||||
(base_list (identifier) @heritage.extends)) @heritage
|
||||
(class_declaration name: (identifier) @heritage.class
|
||||
(base_list (simple_base_type (generic_name (identifier) @heritage.extends)))) @heritage
|
||||
(base_list (generic_name (identifier) @heritage.extends))) @heritage
|
||||
`;
|
||||
|
||||
// Rust queries - works with tree-sitter-rust
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -185,28 +185,58 @@ const isNodeExported = (node: any, name: string, language: string): boolean => {
|
||||
}
|
||||
return false;
|
||||
|
||||
case 'csharp':
|
||||
case 'csharp': {
|
||||
// In C# AST, `modifier` nodes are SIBLINGS of the name node inside the
|
||||
// declaration (e.g. method_declaration, class_declaration). Walking up
|
||||
// from the name node reaches the declaration, then we check its children.
|
||||
const CSHARP_DECL_TYPES = new Set([
|
||||
'method_declaration', 'local_function_statement', 'constructor_declaration',
|
||||
'class_declaration', 'interface_declaration', 'struct_declaration',
|
||||
'enum_declaration', 'record_declaration', 'delegate_declaration',
|
||||
'property_declaration', 'field_declaration', 'event_declaration',
|
||||
'namespace_declaration',
|
||||
]);
|
||||
while (current) {
|
||||
if (current.type === 'modifier' || current.type === 'modifiers') {
|
||||
if (current.text?.includes('public')) return true;
|
||||
if (CSHARP_DECL_TYPES.has(current.type)) {
|
||||
// Check siblings: any child of the declaration that is a modifier with text 'public'
|
||||
for (let i = 0; i < current.childCount; i++) {
|
||||
const child = current.child(i);
|
||||
if (child?.type === 'modifier' && child.text === 'public') return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case 'go':
|
||||
if (name.length === 0) return false;
|
||||
const first = name[0];
|
||||
return first === first.toUpperCase() && first !== first.toLowerCase();
|
||||
|
||||
case 'rust':
|
||||
case 'rust': {
|
||||
// In Rust AST, `visibility_modifier` is a SIBLING of the name node (identifier/type_identifier)
|
||||
// within the declaration node (function_item, struct_item, impl_item, etc.).
|
||||
// Walking up parents from the name node will never hit `visibility_modifier` directly.
|
||||
// Fix: walk up to the declaration node, then check its children for visibility_modifier.
|
||||
const RUST_DECL_TYPES = new Set([
|
||||
'function_item', 'struct_item', 'enum_item', 'trait_item', 'impl_item',
|
||||
'type_item', 'const_item', 'static_item', 'mod_item', 'use_declaration',
|
||||
'associated_type', 'function_signature_item',
|
||||
]);
|
||||
while (current) {
|
||||
if (current.type === 'visibility_modifier') {
|
||||
if (current.text?.includes('pub')) return true;
|
||||
if (RUST_DECL_TYPES.has(current.type)) {
|
||||
for (let i = 0; i < current.childCount; i++) {
|
||||
const child = current.child(i);
|
||||
if (child?.type === 'visibility_modifier' && child.text?.startsWith('pub')) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Kotlin: Default visibility is public (unlike Java)
|
||||
// visibility_modifier is inside modifiers, a sibling of the name node within the declaration
|
||||
@@ -225,9 +255,22 @@ const isNodeExported = (node: any, name: string, language: string): boolean => {
|
||||
// No visibility modifier = public (Kotlin default)
|
||||
return true;
|
||||
|
||||
// C/C++: Functions without 'static' storage class have external linkage
|
||||
// by default, making them globally accessible (equivalent to exported).
|
||||
// Only functions explicitly marked 'static' are file-scoped (not exported).
|
||||
case 'c':
|
||||
case 'cpp':
|
||||
return false;
|
||||
case 'cpp': {
|
||||
let cur = node;
|
||||
while (cur) {
|
||||
if (cur.type === 'function_definition' || cur.type === 'declaration') {
|
||||
const declText: string = (cur.text || '').split('{')[0].split(';')[0];
|
||||
if (/\bstatic\b/.test(declText)) return false;
|
||||
return true;
|
||||
}
|
||||
cur = cur.parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case 'php':
|
||||
// Top-level classes/interfaces/traits are always accessible
|
||||
@@ -297,9 +340,46 @@ const findEnclosingFunctionId = (node: any, filePath: string): string | null =>
|
||||
|
||||
if (['function_declaration', 'function_definition', 'async_function_declaration',
|
||||
'generator_function_declaration', 'function_item'].includes(current.type)) {
|
||||
// Try direct name field (JS/TS/Python/Rust)
|
||||
const nameNode = current.childForFieldName?.('name') ||
|
||||
current.children?.find((c: any) => c.type === 'identifier' || c.type === 'property_identifier');
|
||||
funcName = nameNode?.text;
|
||||
if (nameNode) {
|
||||
funcName = nameNode.text;
|
||||
// C++ template functions: function_definition inside template_declaration
|
||||
// are registered as 'Template' nodes (not 'Function'), so match that label.
|
||||
if (current.type === 'function_definition' && current.parent?.type === 'template_declaration') {
|
||||
label = 'Template';
|
||||
}
|
||||
} else {
|
||||
// C/C++: name is nested in declarator -> function_declarator -> identifier/qualified_identifier
|
||||
const declarator = current.childForFieldName?.('declarator');
|
||||
if (declarator) {
|
||||
const innerDecl = declarator.childForFieldName?.('declarator');
|
||||
if (innerDecl?.type === 'identifier') {
|
||||
funcName = innerDecl.text;
|
||||
// Template function with qualified-style declarator (rare, but check)
|
||||
if (current.parent?.type === 'template_declaration') {
|
||||
label = 'Template';
|
||||
}
|
||||
} else if (innerDecl?.type === 'qualified_identifier') {
|
||||
// C++ qualified name: Foo::bar — captured as 'Method' node
|
||||
const nameIdent = innerDecl.childForFieldName?.('name') ||
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (current.type === 'impl_item') {
|
||||
const funcItem = current.children?.find((c: any) => c.type === 'function_item');
|
||||
if (funcItem) {
|
||||
@@ -359,7 +439,8 @@ const BUILT_INS = new Set([
|
||||
'hasOwnProperty', 'toString', 'valueOf',
|
||||
// Python
|
||||
'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
|
||||
'open', 'read', 'write', 'close', 'append', 'extend', 'update',
|
||||
'append', 'extend', 'update',
|
||||
// NOTE: 'open', 'read', 'write', 'close' removed — these are real C POSIX syscalls
|
||||
'super', 'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr',
|
||||
'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs',
|
||||
// Kotlin stdlib (IMPORTANT: keep in sync with call-processor.ts BUILT_IN_NAMES)
|
||||
@@ -1109,7 +1190,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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user