diff --git a/cli/src/commands/bookmark/create.rs b/cli/src/commands/bookmark/create.rs index ec1d7100efa..3eeeecb16d7 100644 --- a/cli/src/commands/bookmark/create.rs +++ b/cli/src/commands/bookmark/create.rs @@ -13,6 +13,7 @@ // limitations under the License. use clap::builder::NonEmptyStringValueParser; +use clap_complete::ArgValueCandidates; use jj_lib::object_id::ObjectId as _; use jj_lib::op_store::RefTarget; @@ -21,6 +22,7 @@ use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::user_error_with_hint; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Create a new bookmark @@ -34,7 +36,11 @@ pub struct BookmarkCreateArgs { revision: Option, /// The bookmarks to create - #[arg(required = true, value_parser = NonEmptyStringValueParser::new())] + #[arg( + required = true, + value_parser = NonEmptyStringValueParser::new(), + add = ArgValueCandidates::new(complete::push_bookmark_prefix), + )] names: Vec, } diff --git a/cli/src/commands/bookmark/rename.rs b/cli/src/commands/bookmark/rename.rs index e11cf99068f..0d55c5df331 100644 --- a/cli/src/commands/bookmark/rename.rs +++ b/cli/src/commands/bookmark/rename.rs @@ -32,6 +32,7 @@ pub struct BookmarkRenameArgs { old: String, /// The new name of the bookmark + #[arg(add = ArgValueCandidates::new(complete::bookmark_rename_new))] new: String, } diff --git a/cli/src/complete.rs b/cli/src/complete.rs index 01f59c6ece3..1f281180fc4 100644 --- a/cli/src/complete.rs +++ b/cli/src/complete.rs @@ -44,6 +44,39 @@ pub fn local_bookmarks() -> Vec { }) } +pub fn push_bookmark_prefix() -> Vec { + with_jj(|_, config| { + Ok(match config.get::("git.push-bookmark-prefix") { + Ok(prefix) => vec![CompletionCandidate::new(prefix)], + Err(_) => Vec::new(), // user didn't configure a prefix + }) + }) +} + +/// A specific completion function for the `new` argument of `bookmark rename`. +/// It suggests the `old` argument as first candidate, because the user may +/// want to fix a typo. +pub fn bookmark_rename_new() -> Vec { + let mut candidates = Vec::new(); + + let bookmark_to_rename = std::env::args_os() + .rev() + .filter_map(|arg| arg.into_string().ok()) + .filter(|arg| !arg.starts_with("-")) + // The first non-flag argument is the one being completed, the second + // one is the "old" argument. + .nth(1); + if let Some(bookmark) = bookmark_to_rename { + candidates.push(CompletionCandidate::new(bookmark).display_order(Some(0))); + } + with_jj(|_, config| { + if let Ok(prefix) = config.get::("git.push-bookmark-prefix") { + candidates.push(CompletionCandidate::new(prefix).display_order(Some(1))); + } + Ok(candidates) + }) +} + /// Shell out to jj during dynamic completion generation /// /// In case of errors, print them and early return an empty vector. diff --git a/cli/tests/test_completion.rs b/cli/tests/test_completion.rs index 7a268353a7f..6b5827a64da 100644 --- a/cli/tests/test_completion.rs +++ b/cli/tests/test_completion.rs @@ -76,3 +76,26 @@ fn test_global_arg_repository_is_respected() { ); insta::assert_snapshot!(stdout, @"aaa"); } + +#[test] +fn test_push_bookmark_prefix() { + let mut test_env = TestEnvironment::default(); + + test_env.add_config(r#"git.push-bookmark-prefix = "fancy-prefix/""#); + + test_env.add_env_var("COMPLETE", "fish"); + + let stdout = test_env.jj_cmd_success( + test_env.env_root(), + &["--", "jj", "bookmark", "create", "f"], + ); + insta::assert_snapshot!(stdout, @"fancy-prefix/"); + let stdout = test_env.jj_cmd_success( + test_env.env_root(), + &["--", "jj", "bookmark", "rename", "fancy-old-name", "f"], + ); + insta::assert_snapshot!(stdout, @r" + fancy-old-name + fancy-prefix/ + "); +}