use crate::keywords::{ has_export, is_function_end, is_function_start, is_procedure_end, is_procedure_start, }; use crate::models::{ParsedProcedure, SourceRange}; pub fn extract_procedures(source: &str) -> Vec { let mut result = Vec::new(); let mut current_index: Option = None; for (index, line) in source.lines().enumerate() { let line_no = index + 1; if is_procedure_start(line) || is_function_start(line) { let is_function = is_function_start(line); let name = extract_routine_name(line, is_function); result.push(ParsedProcedure { name, export: has_export(line), is_function, parameters: extract_parameters(line), source_range: SourceRange { line_start: line_no, line_end: line_no, column_start: 1, column_end: line.len() + 1, }, }); current_index = Some(result.len() - 1); } else if routine_ended(line) { if let Some(procedure_index) = current_index.take() { result[procedure_index].source_range.line_end = line_no; result[procedure_index].source_range.column_end = line.len() + 1; } } } result } pub fn routine_ended(line: &str) -> bool { is_procedure_end(line) || is_function_end(line) } pub fn extract_name_from_header(line: &str) -> String { line.split_whitespace() .nth(1) .unwrap_or("") .split('(') .next() .unwrap_or("") .to_string() } fn extract_routine_name(line: &str, is_function: bool) -> String { let trimmed = line.trim(); let prefixes = if is_function { vec!["Функция", "Function"] } else { vec!["Процедура", "Procedure"] }; for prefix in prefixes { if trimmed.to_lowercase().starts_with(&prefix.to_lowercase()) { return trimmed[prefix.len()..] .trim() .split('(') .next() .unwrap_or("") .trim() .to_string(); } } String::new() } fn extract_parameters(line: &str) -> Vec { let Some(start) = line.find('(') else { return Vec::new(); }; let Some(end) = line[start + 1..].find(')') else { return Vec::new(); }; let params = &line[start + 1..start + 1 + end]; params .split(',') .map(|p| p.trim().to_string()) .filter(|p| !p.is_empty()) .collect() }