diff --git a/src/cortex-cli/src/alias_cmd.rs b/src/cortex-cli/src/alias_cmd.rs index f1df82c7..64d0d02d 100644 --- a/src/cortex-cli/src/alias_cmd.rs +++ b/src/cortex-cli/src/alias_cmd.rs @@ -12,6 +12,10 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::PathBuf; +const DESCRIPTION_PREFIX: &str = " Description: "; +const DESCRIPTION_CONTINUATION_PREFIX: &str = " "; +const ALIAS_LIST_DESCRIPTION_PREFIX: &str = " "; + /// Alias CLI command. #[derive(Debug, Parser)] pub struct AliasCli { @@ -137,6 +141,25 @@ fn save_aliases(config: &AliasConfig) -> Result<()> { Ok(()) } +fn format_multiline_prefixed_value( + first_prefix: &str, + continuation_prefix: &str, + value: &str, +) -> String { + let mut lines = value.lines(); + let Some(first_line) = lines.next() else { + return first_prefix.to_string(); + }; + + let mut formatted = format!("{first_prefix}{first_line}"); + for line in lines { + formatted.push('\n'); + formatted.push_str(continuation_prefix); + formatted.push_str(line); + } + formatted +} + impl AliasCli { /// Run the alias command. pub async fn run(self) -> Result<()> { @@ -172,7 +195,14 @@ async fn run_set(args: AliasSetArgs) -> Result<()> { println!("Alias '{}' set to: {}", args.name, args.command); if let Some(desc) = &args.description { - println!(" Description: {}", desc); + println!( + "{}", + format_multiline_prefixed_value( + DESCRIPTION_PREFIX, + DESCRIPTION_CONTINUATION_PREFIX, + desc + ) + ); } Ok(()) @@ -203,7 +233,14 @@ async fn run_list(args: AliasListArgs) -> Result<()> { for alias in aliases { println!(" {} = {}", alias.name, alias.command); if let Some(desc) = &alias.description { - println!(" {}", desc); + println!( + "{}", + format_multiline_prefixed_value( + ALIAS_LIST_DESCRIPTION_PREFIX, + ALIAS_LIST_DESCRIPTION_PREFIX, + desc + ) + ); } } @@ -265,7 +302,14 @@ async fn run_show(args: AliasShowArgs) -> Result<()> { println!("{}", "-".repeat(40)); println!(" Command: {}", alias.command); if let Some(desc) = &alias.description { - println!(" Description: {}", desc); + println!( + "{}", + format_multiline_prefixed_value( + DESCRIPTION_PREFIX, + DESCRIPTION_CONTINUATION_PREFIX, + desc + ) + ); } Ok(()) @@ -616,4 +660,26 @@ command = "list" Some("Command with special chars: <>&".to_string()) ); } + + #[test] + fn multiline_description_lines_keep_list_indent() { + let formatted = + format_multiline_prefixed_value(" ", " ", "First line\nSecond line"); + + assert_eq!(formatted, " First line\n Second line"); + } + + #[test] + fn multiline_description_lines_align_after_description_label() { + let formatted = format_multiline_prefixed_value( + " Description: ", + " ", + "First line\nSecond line", + ); + + assert_eq!( + formatted, + " Description: First line\n Second line" + ); + } } diff --git a/src/cortex-cli/tests/alias_multiline_description.rs b/src/cortex-cli/tests/alias_multiline_description.rs new file mode 100644 index 00000000..6b0c1f0b --- /dev/null +++ b/src/cortex-cli/tests/alias_multiline_description.rs @@ -0,0 +1,52 @@ +use std::path::Path; +use std::process::Command; + +fn cortex_command(home: &Path) -> Command { + let mut command = Command::new(env!("CARGO_BIN_EXE_Cortex")); + command.env("HOME", home); + command.env("USERPROFILE", home); + command.env("APPDATA", home.join("AppData").join("Roaming")); + command +} + +fn run_success(command: &mut Command) -> String { + let output = command.output().expect("run Cortex"); + assert!( + output.status.success(), + "command failed\nstatus: {}\nstdout:\n{}\nstderr:\n{}", + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + String::from_utf8_lossy(&output.stdout).to_string() +} + +#[test] +fn alias_descriptions_indent_every_multiline_line() { + let temp = tempfile::tempdir().expect("temp dir"); + let description = "First line\nSecond line"; + + let set_output = run_success( + cortex_command(temp.path()) + .args(["alias", "set", "bounty_alias_demo", "version", "-d"]) + .arg(description) + .arg("-f"), + ); + assert!( + set_output.contains(" Description: First line\n Second line"), + "{set_output}" + ); + + let list_output = run_success(cortex_command(temp.path()).args(["alias", "list"])); + assert!( + list_output.contains(" First line\n Second line"), + "{list_output}" + ); + + let show_output = + run_success(cortex_command(temp.path()).args(["alias", "show", "bounty_alias_demo"])); + assert!( + show_output.contains(" Description: First line\n Second line"), + "{show_output}" + ); +}