use crate::app::tab::Tab; use crate::app::TextEditor; use std::fs; use std::path::PathBuf; pub(crate) fn new_file(app: &mut TextEditor) { app.add_new_tab(); } fn is_text_file(path: &PathBuf) -> bool { if let Some(extension) = path.extension().and_then(|s| s.to_str()) { matches!( extension.to_lowercase().as_str(), "txt" | "md" | "markdown" | "rs" | "py" | "js" | "ts" | "tsx" | "jsx" | "c" | "cpp" | "cc" | "cxx" | "h" | "hpp" | "java" | "go" | "php" | "rb" | "cs" | "swift" | "kt" | "scala" | "sh" | "bash" | "zsh" | "fish" | "html" | "htm" | "xml" | "css" | "scss" | "sass" | "json" | "yaml" | "yml" | "toml" | "sql" | "lua" | "vim" | "dockerfile" | "makefile" | "gitignore" | "conf" | "cfg" | "ini" | "log" | "csv" | "tsv" ) } else { // Files without extensions might be text files, but let's be conservative // and only include them if they're small and readable if let Ok(metadata) = fs::metadata(path) { metadata.len() < 1024 * 1024 // Only consider files smaller than 1MB } else { false } } } pub(crate) fn open_files_from_directory( app: &mut TextEditor, dir_path: PathBuf, ) -> Result { if !dir_path.is_dir() { return Err(format!("{} is not a directory", dir_path.display())); } let entries = fs::read_dir(&dir_path) .map_err(|e| format!("Failed to read directory {}: {}", dir_path.display(), e))?; let mut opened_count = 0; let mut text_files: Vec = Vec::new(); // Collect all text files in the directory for entry in entries { let entry = entry.map_err(|e| format!("Failed to read directory entry: {}", e))?; let path = entry.path(); if path.is_file() && is_text_file(&path) { text_files.push(path); } } // Sort files by name for consistent ordering text_files.sort(); // Open each text file for file_path in text_files { match open_file_from_path(app, file_path.clone()) { Ok(()) => opened_count += 1, Err(e) => eprintln!("Warning: {}", e), } } if opened_count == 0 { Err(format!( "No text files found in directory {}", dir_path.display() )) } else { Ok(opened_count) } } pub(crate) fn open_file(app: &mut TextEditor) { if let Some(path) = rfd::FileDialog::new() .add_filter("Text files", &["*"]) .pick_file() { match fs::read_to_string(&path) { Ok(content) => { let should_replace_current_tab = if let Some(active_tab) = app.get_active_tab() { active_tab.file_path.is_none() && active_tab.content.is_empty() && !active_tab.is_modified } else { false }; if should_replace_current_tab { if let Some(active_tab) = app.get_active_tab_mut() { let title = path .file_name() .and_then(|n| n.to_str()) .unwrap_or("Untitled"); active_tab.content = content; active_tab.file_path = Some(path.to_path_buf()); active_tab.title = title.to_string(); active_tab.mark_as_saved(); } app.text_needs_processing = true; } else { let new_tab = Tab::new_with_file(content, path); app.tabs.push(new_tab); app.active_tab_index = app.tabs.len() - 1; app.text_needs_processing = true; } if app.show_find && !app.find_query.is_empty() { app.update_find_matches(); } if let Err(e) = app.save_state_cache() { eprintln!("Failed to save state cache: {e}"); } } Err(err) => { eprintln!("Failed to open file: {err}"); } } } } pub(crate) fn open_file_from_path(app: &mut TextEditor, path: PathBuf) -> Result<(), String> { match fs::read_to_string(&path) { Ok(content) => { let should_replace_current_tab = if let Some(active_tab) = app.get_active_tab() { active_tab.file_path.is_none() && active_tab.content.is_empty() && !active_tab.is_modified } else { false }; if should_replace_current_tab { if let Some(active_tab) = app.get_active_tab_mut() { let title = path .file_name() .and_then(|n| n.to_str()) .unwrap_or("Untitled"); active_tab.content = content; active_tab.file_path = Some(path.to_path_buf()); active_tab.title = title.to_string(); active_tab.mark_as_saved(); } app.text_needs_processing = true; } else { let new_tab = Tab::new_with_file(content, path); app.tabs.push(new_tab); app.active_tab_index = app.tabs.len() - 1; app.text_needs_processing = true; } if app.show_find && !app.find_query.is_empty() { app.update_find_matches(); } if let Err(e) = app.save_state_cache() { eprintln!("Failed to save state cache: {e}"); } Ok(()) } Err(err) => Err(format!("Failed to open file {}: {}", path.display(), err)), } } pub(crate) fn save_file(app: &mut TextEditor) { if let Some(active_tab) = app.get_active_tab() { if let Some(path) = &active_tab.file_path { save_to_path(app, path.to_path_buf()); } else { save_as_file(app); } } } pub(crate) fn save_as_file(app: &mut TextEditor) { if let Some(path) = rfd::FileDialog::new() .add_filter("Text files", &["*"]) .save_file() { save_to_path(app, path); } } pub(crate) fn save_to_path(app: &mut TextEditor, path: PathBuf) { if let Some(active_tab) = app.get_active_tab_mut() { match fs::write(&path, &active_tab.content) { Ok(()) => { let title = path .file_name() .and_then(|n| n.to_str()) .unwrap_or("Untitled"); active_tab.file_path = Some(path.to_path_buf()); active_tab.title = title.to_string(); active_tab.mark_as_saved(); if let Err(e) = app.save_state_cache() { eprintln!("Failed to save state cache: {e}"); } } Err(err) => { eprintln!("Failed to save file: {err}"); } } } }