diff --git a/src/options.rs b/src/options.rs index e5866455..d49c4d65 100644 --- a/src/options.rs +++ b/src/options.rs @@ -196,7 +196,13 @@ pub struct SkimOptions { /// See **nth** for the details #[cfg_attr( feature = "cli", - arg(long, default_value = "", help_heading = "Search", value_delimiter = ',') + arg( + long, + default_value = "", + help_heading = "Search", + value_delimiter = ',', + allow_hyphen_values = true, + ) )] pub with_nth: Vec, diff --git a/src/options_tests.rs b/src/options_tests.rs index 754b8dab..4a225b38 100644 --- a/src/options_tests.rs +++ b/src/options_tests.rs @@ -2,6 +2,7 @@ //! and history initialization, which apply defaults and cross-option rules. use super::*; +use crate::field::FieldRange; use crate::item::RankCriteria; use crate::tui::statusline::InfoDisplay; @@ -287,3 +288,38 @@ fn build_history_file_adds_history_keybindings() { let _ = std::fs::remove_file(&qpath); } + +/// Helper: parse real CLI args with no env influence. +fn parse_args(args: &[&str]) -> Result { + SkimOptions::merge_args_and_parse( + "sk".to_string(), + None, + None, + args.iter().map(|s| (*s).to_string()), + None, + ) +} + +#[test] +fn negative_field_indices_parse_for_every_nth_flag() { + // All three flags document the same `nth` syntax, which includes `-1` for the + // last field; a space-separated negative value must not be read as a flag. + for flag in ["--nth", "--with-nth", "--hide-nth"] { + let opts = parse_args(&[flag, "-1"]).unwrap_or_else(|e| panic!("{flag} -1 failed to parse: {e}")); + let got = match flag { + "--nth" => &opts.nth, + "--with-nth" => &opts.with_nth, + _ => &opts.hide_nth, + }; + assert_eq!(got, &vec!["-1".to_string()], "{flag}"); + assert_eq!( + FieldRange::from_str(&got[0]), + Some(FieldRange::Single(-1)), + "{flag} should resolve to the last field" + ); + } + + // ...and a negative index inside a comma-separated list. + let opts = parse_args(&["--with-nth", "2,-1"]).expect("--with-nth 2,-1 should parse"); + assert_eq!(opts.with_nth, vec!["2".to_string(), "-1".to_string()]); +} diff --git a/tests/cli.rs b/tests/cli.rs index 4d32250e..42308bd7 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -128,6 +128,19 @@ fn filter_mode_no_sort_preserves_input_order() { ); } +#[test] +fn with_nth_accepts_space_separated_negative_index() { + // `--with-nth -1` (space form) used to be parsed as a missing value, while + // `--nth -1` and `--with-nth=-1` both worked. + let (code, stdout, stderr) = run_sk_argv("a b c", &["-f", "c", "--with-nth", "-1"], &[]); + assert_eq!(code, Some(0), "stderr: {stderr}"); + assert_eq!(stdout.trim_end(), "a b c"); + + // The space form and the `=` form must agree. + let (code_eq, stdout_eq, _) = run_sk_argv("a b c", &["-f", "c", "--with-nth=-1"], &[]); + assert_eq!((code, stdout), (code_eq, stdout_eq)); +} + #[test] fn select_1_with_output_format() { // --output-format renders the selected item through the printf branch.