diff --git a/cli/src/commands/bookmark/list.rs b/cli/src/commands/bookmark/list.rs index fdd6cbe419..1279b4c860 100644 --- a/cli/src/commands/bookmark/list.rs +++ b/cli/src/commands/bookmark/list.rs @@ -14,6 +14,7 @@ use std::collections::HashSet; +use clap_complete::ArgValueCandidates; use itertools::Itertools; use jj_lib::git; use jj_lib::revset::RevsetExpression; @@ -24,6 +25,7 @@ use crate::cli_util::RevisionArg; use crate::command_error::CommandError; use crate::commit_templater::CommitTemplateLanguage; use crate::commit_templater::RefName; +use crate::complete; use crate::ui::Ui; /// List bookmarks and their targets @@ -57,6 +59,7 @@ pub struct BookmarkListArgs { value_name = "REMOTE", conflicts_with_all = ["all_remotes"], value_parser = StringPattern::parse, + add = ArgValueCandidates::new(complete::git_remotes), )] remotes: Option>, diff --git a/cli/src/commands/git/fetch.rs b/cli/src/commands/git/fetch.rs index 86bf9b77c9..21b21f1d39 100644 --- a/cli/src/commands/git/fetch.rs +++ b/cli/src/commands/git/fetch.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use clap_complete::ArgValueCandidates; use itertools::Itertools; use jj_lib::repo::Repo; use jj_lib::settings::ConfigResultExt as _; @@ -21,6 +22,7 @@ use jj_lib::str_util::StringPattern; use crate::cli_util::CommandHelper; use crate::command_error::CommandError; use crate::commands::git::get_single_remote; +use crate::complete; use crate::git_util::get_git_repo; use crate::git_util::git_fetch; use crate::ui::Ui; @@ -39,7 +41,11 @@ pub struct GitFetchArgs { branch: Vec, /// The remote to fetch from (only named remotes are supported, can be /// repeated) - #[arg(long = "remote", value_name = "REMOTE")] + #[arg( + long = "remote", + value_name = "REMOTE", + add = ArgValueCandidates::new(complete::git_remotes), + )] remotes: Vec, /// Fetch from all remotes #[arg(long, conflicts_with = "remotes")] diff --git a/cli/src/commands/git/push.rs b/cli/src/commands/git/push.rs index 127ccf368b..9be3eab78c 100644 --- a/cli/src/commands/git/push.rs +++ b/cli/src/commands/git/push.rs @@ -18,6 +18,7 @@ use std::io; use std::io::Write; use clap::ArgGroup; +use clap_complete::ArgValueCandidates; use itertools::Itertools; use jj_lib::backend::CommitId; use jj_lib::git; @@ -46,6 +47,7 @@ use crate::command_error::user_error; use crate::command_error::user_error_with_hint; use crate::command_error::CommandError; use crate::commands::git::get_single_remote; +use crate::complete; use crate::formatter::Formatter; use crate::git_util::get_git_repo; use crate::git_util::map_git_error; @@ -76,7 +78,7 @@ use crate::ui::Ui; #[command(group(ArgGroup::new("what").args(&["all", "deleted", "tracked"]).conflicts_with("specific")))] pub struct GitPushArgs { /// The remote to push to (only named remotes are supported) - #[arg(long)] + #[arg(long, add = ArgValueCandidates::new(complete::git_remotes))] remote: Option, /// Push only this bookmark, or bookmarks matching a pattern (can be /// repeated) diff --git a/cli/src/commands/git/remote/remove.rs b/cli/src/commands/git/remote/remove.rs index f2dca800db..850ebdba9a 100644 --- a/cli/src/commands/git/remote/remove.rs +++ b/cli/src/commands/git/remote/remove.rs @@ -12,11 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +use clap_complete::ArgValueCandidates; use jj_lib::git; use jj_lib::repo::Repo; use crate::cli_util::CommandHelper; use crate::command_error::CommandError; +use crate::complete; use crate::git_util::get_git_repo; use crate::ui::Ui; @@ -24,6 +26,7 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] pub struct GitRemoteRemoveArgs { /// The remote's name + #[arg(add = ArgValueCandidates::new(complete::git_remotes))] remote: String, } diff --git a/cli/src/commands/git/remote/rename.rs b/cli/src/commands/git/remote/rename.rs index 94f966825d..ff8cc0a6c0 100644 --- a/cli/src/commands/git/remote/rename.rs +++ b/cli/src/commands/git/remote/rename.rs @@ -12,11 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +use clap_complete::ArgValueCandidates; use jj_lib::git; use jj_lib::repo::Repo; use crate::cli_util::CommandHelper; use crate::command_error::CommandError; +use crate::complete; use crate::git_util::get_git_repo; use crate::ui::Ui; @@ -24,6 +26,7 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] pub struct GitRemoteRenameArgs { /// The name of an existing remote + #[arg(add = ArgValueCandidates::new(complete::git_remotes))] old: String, /// The desired name for `old` new: String, diff --git a/cli/src/commands/git/remote/set_url.rs b/cli/src/commands/git/remote/set_url.rs index cee2da0559..770efa7f50 100644 --- a/cli/src/commands/git/remote/set_url.rs +++ b/cli/src/commands/git/remote/set_url.rs @@ -12,11 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +use clap_complete::ArgValueCandidates; use jj_lib::git; use jj_lib::repo::Repo; use crate::cli_util::CommandHelper; use crate::command_error::CommandError; +use crate::complete; use crate::git_util::get_git_repo; use crate::ui::Ui; @@ -24,6 +26,7 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] pub struct GitRemoteSetUrlArgs { /// The remote's name + #[arg(add = ArgValueCandidates::new(complete::git_remotes))] remote: String, /// The desired url for `remote` url: String, diff --git a/cli/src/complete.rs b/cli/src/complete.rs index 681441f6dd..ad23819397 100644 --- a/cli/src/complete.rs +++ b/cli/src/complete.rs @@ -43,6 +43,25 @@ pub fn local_bookmarks() -> Vec { }) } +pub fn git_remotes() -> Vec { + with_jj(|mut jj| { + let output = jj + .arg("git") + .arg("remote") + .arg("list") + .output() + .map_err(user_error)?; + + let stdout = String::from_utf8_lossy(&output.stdout); + + Ok(stdout + .lines() + .filter_map(|line| line.split_once(' ').map(|(name, _url)| name)) + .map(CompletionCandidate::new) + .collect()) + }) +} + /// 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 20d5c341fc..b9c3441998 100644 --- a/cli/tests/test_completion.rs +++ b/cli/tests/test_completion.rs @@ -115,3 +115,52 @@ fn test_completions_are_generated() { let stdout = test_env.jj_cmd_success(test_env.env_root(), &["--"]); assert!(stdout.starts_with("complete --keep-order --exclusive --command jj --arguments")); } + +#[test] +fn test_remote_names() { + let mut test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init"]); + + test_env.jj_cmd_ok( + test_env.env_root(), + &["git", "remote", "add", "origin", "git@git.local:user/repo"], + ); + + test_env.add_env_var("COMPLETE", "fish"); + + let stdout = test_env.jj_cmd_success( + test_env.env_root(), + &["--", "jj", "git", "remote", "remove", "o"], + ); + insta::assert_snapshot!(stdout, @r"origin"); + + let stdout = test_env.jj_cmd_success( + test_env.env_root(), + &["--", "jj", "git", "remote", "rename", "o"], + ); + insta::assert_snapshot!(stdout, @r"origin"); + + let stdout = test_env.jj_cmd_success( + test_env.env_root(), + &["--", "jj", "git", "remote", "set-url", "o"], + ); + insta::assert_snapshot!(stdout, @r"origin"); + + let stdout = test_env.jj_cmd_success( + test_env.env_root(), + &["--", "jj", "git", "push", "--remote", "o"], + ); + insta::assert_snapshot!(stdout, @r"origin"); + + let stdout = test_env.jj_cmd_success( + test_env.env_root(), + &["--", "jj", "git", "fetch", "--remote", "o"], + ); + insta::assert_snapshot!(stdout, @r"origin"); + + let stdout = test_env.jj_cmd_success( + test_env.env_root(), + &["--", "jj", "bookmark", "list", "--remote", "o"], + ); + insta::assert_snapshot!(stdout, @r"origin"); +}