diff --git a/Cargo.lock b/Cargo.lock index 4ab3fd8..91f04bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,6 +10,7 @@ dependencies = [ "chrono", "clap", "comfy-table", + "dialoguer", "dirs", "serde", "serde_json", @@ -188,6 +189,19 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "console" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.61.2", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -217,6 +231,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "dialoguer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f104b501bf2364e78d0d3974cbc774f738f5865306ed128e1e0d7499c0ad96" +dependencies = [ + "console", + "shell-words", + "tempfile", + "zeroize", +] + [[package]] name = "dirs" version = "6.0.0" @@ -247,6 +273,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "equivalent" version = "1.0.2" @@ -592,6 +624,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.3.0" @@ -952,3 +990,9 @@ name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/Cargo.toml b/Cargo.toml index fa14d45..8016038 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ anyhow = "1.0" thiserror = "2.0.17" dirs = "6.0.0" comfy-table = "7.1" +dialoguer = "0.12.0" [dev-dependencies] tempfile = "3.8" \ No newline at end of file diff --git a/README.md b/README.md index 3fcb768..4d5855e 100644 --- a/README.md +++ b/README.md @@ -24,33 +24,41 @@ cargo install alx ## Quick Start -### 1. Initialize +### Initialize ```bash alx init ``` -### 2. Add to your shell config +Choose your shell. -First, run the `alx info` command, and then check the path for `Shell aliases`. - -Add the following line to your shell configuration file: +```bash +Initialized alx configuration at: "/your_alx_path/alx" +Select your shell: +> zsh (default) + bash + fis +``` -**Bash** (`~/.bashrc`): +Choose whether to write the 'alx' alias configuration to your shell file. The default is No. ```bash -[ -f ~/your_alx_path/aliases.sh ] && source ~/your_alx_path/aliases.sh +To enable aliases, add the following line to your shell config: + # Add to '/your/.zshrc' + [ -f '/your_alx_path/alx/shell/aliases.sh' ] && source '/your_alx_path/alx/shell/aliases.sh' +Do you want to add this line to '/your/.zshrc' automatically? [y/N] ``` -**Zsh** (`~/.zshrc`): +See below for manual setup. ```bash -[ -f ~/your_alias_path/aliases.sh ] && source ~/your_alias_path/aliases.sh -``` +# Bash (`~/.bashrc`): +[ -f ~/your_alx_path/aliases.sh ] && source ~/your_alx_path/aliases.sh -**Fish** (`~/.config/fish/config.fish`): +# Zsh (`~/.zshrc`): +[ -f ~/your_alias_path/aliases.sh ] && source ~/your_alias_path/aliases.sh -```fish +# Fish (`~/.config/fish/config.fish`): source ~/your_alias_path/aliases.sh ``` diff --git a/src/command.rs b/src/command.rs index 63de7e5..cc5a590 100644 --- a/src/command.rs +++ b/src/command.rs @@ -11,6 +11,7 @@ use crate::shell::{ShellHandler, ShellType}; use comfy_table::{ Cell, ContentArrangement, Table, modifiers::UTF8_ROUND_CORNERS, presets::UTF8_BORDERS_ONLY, }; +use dialoguer::{Confirm, Select}; use std::fs; fn sync_aliases() -> Result<()> { @@ -49,32 +50,112 @@ pub fn init() -> Result<()> { "✓ Initialized alx configuration at: {:?}", config_manager.config_dir() ); - println!("\nNext steps:"); - println!(" 1. Add aliases with: alx add "); - println!(" 2. Add the following line to your shell config file:"); - let shell = ShellDetector::detect().ok(); - if let Some(shell_type) = shell { - let handler: Box = match shell_type { - ShellType::Bash => Box::new(BashHandler::new()), - ShellType::Zsh => Box::new(ZshHandler::new()), - ShellType::Fish => Box::new(FishHandler::new()), - }; + // Detect default shell + let default_shell = ShellDetector::detect().ok(); + + // Create shell options with default shell at the top + let mut shell_options = vec![]; + if let Some(default) = default_shell { + shell_options.push(format!("{} (default)", default.as_str())); + } + + // Add other shells + let all_shells = vec![ShellType::Bash, ShellType::Zsh, ShellType::Fish]; + for shell in all_shells { + if Some(shell) != default_shell { + shell_options.push(shell.as_str().to_string()); + } + } - if let Ok(config_file) = handler.config_file_path() { - println!("\n # Add to {:?}", config_file); + // Prompt user to select shell + let selection = Select::new() + .with_prompt("Select your shell") + .items(&shell_options) + .default(0) + .interact() + .map_err(|e| error::AlxError::ConfigError(format!("Failed to select shell: {}", e)))?; + + // Parse selected shell + let selected_shell = if let (0, Some(default)) = (selection, default_shell) { + default + } else { + let shell_name = shell_options[selection].split_whitespace().next().unwrap(); + match shell_name { + "bash" => ShellType::Bash, + "zsh" => ShellType::Zsh, + "fish" => ShellType::Fish, + _ => unreachable!(), } + }; + + let handler: Box = match selected_shell { + ShellType::Bash => Box::new(BashHandler::new()), + ShellType::Zsh => Box::new(ZshHandler::new()), + ShellType::Fish => Box::new(FishHandler::new()), + }; + + let shell_aliases_file = config_manager.shell_aliases_file(); + let aliases_path = shell_aliases_file.display(); + + let source_line = if selected_shell == ShellType::Fish { + format!("source '{}'", aliases_path) + } else { + format!("[ -f '{}' ] && source '{}'", aliases_path, aliases_path) + }; - let shell_aliases_file = config_manager.shell_aliases_file(); - let aliases_path = shell_aliases_file.display(); + // Ask if user wants to add source line automatically + let config_file = handler.config_file_path()?; + + println!("\nTo enable aliases, add the following line to your shell config:"); + println!(" # Add to '{}'", config_file.display()); + println!(" {}", source_line); + + let should_add = Confirm::new() + .with_prompt(format!( + "Do you want to add this line to '{}' automatically?", + config_file.display() + )) + .default(false) + .interact() + .map_err(|e| error::AlxError::ConfigError(format!("Failed to confirm: {}", e)))?; + + if should_add { + // Add source line to shell config + let mut file_content = if config_file.exists() { + fs::read_to_string(&config_file)? + } else { + String::new() + }; - if shell_type == ShellType::Fish { - println!(" source {}", aliases_path); + // Check if the line already exists + if file_content.contains(&source_line) { + println!( + "✓ Source line already exists in '{}'", + config_file.display() + ); } else { - println!(" [ -f {} ] && source {}", aliases_path, aliases_path); + // Add newline if file doesn't end with one + if !file_content.is_empty() && !file_content.ends_with('\n') { + file_content.push('\n'); + } + + // Add comment and source line + file_content.push_str("\n# alx - alias manager\n"); + file_content.push_str(&source_line); + file_content.push('\n'); + + fs::write(&config_file, file_content)?; + println!("✓ Added source line to '{}'", config_file.display()); + println!("\nPlease restart your shell or run:"); + println!(" source '{}'", config_file.display()); } } + println!("\nNext steps:"); + println!(" 1. Add aliases with: alx add "); + println!(" 2. Run 'alx list' to see your aliases"); + Ok(()) }