diff --git a/crates/flow/tests/integration_tests.rs b/crates/flow/tests/integration_tests.rs index 94d89b0..d3fa5fe 100644 --- a/crates/flow/tests/integration_tests.rs +++ b/crates/flow/tests/integration_tests.rs @@ -415,8 +415,6 @@ async fn test_parse_rust_code() { let output = result.unwrap(); let symbols = extract_symbols(&output); - // Note: Currently only extracts functions, not structs/classes - // TODO: Add struct/class extraction in future if !symbols.is_empty() { let symbol_names: Vec = symbols .iter() @@ -426,15 +424,17 @@ async fn test_parse_rust_code() { }) .collect(); - // Look for functions that should be extracted - let found_function = symbol_names.iter().any(|name| { + // Look for functions and structs/classes that should be extracted + let found_function_or_struct = symbol_names.iter().any(|name| { name.contains("main") || name.contains("process_user") || name.contains("calculate_total") + || name.contains("User") + || name.contains("Role") }); assert!( - found_function, - "Should find at least one function (main, process_user, or calculate_total). Found: {:?}", + found_function_or_struct, + "Should find at least one function or struct (main, process_user, calculate_total, User, or Role). Found: {:?}", symbol_names ); } else { diff --git a/crates/language/src/lib.rs b/crates/language/src/lib.rs index 721ddd6..7709c0e 100644 --- a/crates/language/src/lib.rs +++ b/crates/language/src/lib.rs @@ -1721,17 +1721,17 @@ pub fn from_extension(path: &Path) -> Option { } // Handle extensionless files or files with unknown extensions - if let Some(_file_name) = path.file_name().and_then(|n| n.to_str()) { + if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) { // 1. Check if the full filename matches a known extension (e.g. .bashrc) #[cfg(any(feature = "bash", feature = "all-parsers"))] - if constants::BASH_EXTS.contains(&_file_name) { + if constants::BASH_EXTS.contains(&file_name) { return Some(SupportLang::Bash); } // 2. Check known extensionless file names #[cfg(any(feature = "bash", feature = "all-parsers", feature = "ruby"))] for (name, lang) in constants::LANG_RELATIONSHIPS_WITH_NO_EXTENSION { - if *name == _file_name { + if *name == file_name { return Some(*lang); } } diff --git a/crates/services/src/conversion.rs b/crates/services/src/conversion.rs index 37d65e4..52ac444 100644 --- a/crates/services/src/conversion.rs +++ b/crates/services/src/conversion.rs @@ -67,6 +67,13 @@ pub fn extract_basic_metadata( } } + // Extract class and struct definitions + if let Ok(class_matches) = extract_classes(&root_node, &document.language) { + for (name, info) in class_matches { + metadata.defined_symbols.insert(name, info); + } + } + // Extract import statements if let Ok(imports) = extract_imports(&root_node, &document.language) { for (name, info) in imports { @@ -117,6 +124,62 @@ fn extract_functions(root_node: &Node) -> ServiceResult( + root_node: &Node, + language: &SupportLang, +) -> ServiceResult> { + let mut classes = thread_utilities::get_map(); + + // Select language-specific patterns paired with their SymbolKind + let patterns: Vec<(&str, SymbolKind)> = match language { + SupportLang::Rust => vec![ + ("struct $NAME { $$$BODY }", SymbolKind::Struct), // braced struct + ("struct $NAME($$$FIELDS)", SymbolKind::Struct), // tuple struct + ("struct $NAME", SymbolKind::Struct), // unit struct + ], + SupportLang::Go => vec![ + ("type $NAME struct { $$$BODY }", SymbolKind::Struct), // Go struct + ], + SupportLang::JavaScript | SupportLang::TypeScript => vec![ + ("class $NAME { $$$BODY }", SymbolKind::Class), // JS/TS class + ("interface $NAME { $$$BODY }", SymbolKind::Interface), // TS interface + ], + SupportLang::Python => vec![ + ("class $NAME: $$$BODY", SymbolKind::Class), // simple Python class + ("class $NAME($$$PARAMS): $$$BODY", SymbolKind::Class), // Python class with bases + ], + _ => vec![ + // Generic fallbacks for C++, Java, C#, etc. + ("struct $NAME { $$$BODY }", SymbolKind::Struct), + ("class $NAME { $$$BODY }", SymbolKind::Class), + ("interface $NAME { $$$BODY }", SymbolKind::Interface), + ], + }; + + for (pattern, kind) in &patterns { + for node_match in root_node.find_all(*pattern) { + if let Some(name_node) = node_match.get_env().get_match("NAME") { + let symbol_name = name_node.text().to_string(); + let position = name_node.start_pos(); + + let symbol_info = SymbolInfo { + name: symbol_name.clone(), + kind: kind.clone(), + position, + scope: "global".to_string(), // Simplified for now + visibility: Visibility::Public, // Simplified for now + }; + + classes.insert(symbol_name, symbol_info); + } + } + } + + Ok(classes) +} + /// Extract import statements using language-specific patterns #[cfg(feature = "matching")] fn extract_imports( @@ -254,6 +317,7 @@ pub fn node_kind_to_symbol_kind(node_kind: &str) -> SymbolKind { match node_kind { "function_declaration" | "function_definition" => SymbolKind::Function, "class_declaration" | "class_definition" => SymbolKind::Class, + "struct_declaration" | "struct_definition" => SymbolKind::Struct, "interface_declaration" => SymbolKind::Interface, "variable_declaration" | "let_declaration" => SymbolKind::Variable, "const_declaration" | "constant" => SymbolKind::Constant, diff --git a/crates/services/src/types.rs b/crates/services/src/types.rs index c9fdd9a..f48912d 100644 --- a/crates/services/src/types.rs +++ b/crates/services/src/types.rs @@ -527,6 +527,7 @@ pub enum ExecutionStrategy { pub enum SymbolKind { Function, Class, + Struct, Interface, Variable, Constant, diff --git a/get_comments.js b/get_comments.js new file mode 100644 index 0000000..bae3ae1 --- /dev/null +++ b/get_comments.js @@ -0,0 +1,33 @@ +const https = require('https'); + +const options = { + hostname: 'api.github.com', + path: '/repos/knitli/thread/pulls/comments?pull_number=127', + method: 'GET', + headers: { + 'User-Agent': 'node.js' + } +}; + +const req = https.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + const comments = JSON.parse(data); + comments.forEach(c => { + console.log(`Comment ID: ${c.id}`); + console.log(`Path: ${c.path}`); + console.log(`Line: ${c.line || c.original_line}`); + console.log(`Body: ${c.body}`); + console.log('-------------------'); + }); + }); +}); + +req.on('error', (e) => { + console.error(e); +}); + +req.end();