From dd6479f104818513906b346e8909d76e956db038 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Fri, 15 Nov 2024 14:59:54 +0100 Subject: [PATCH] completion: teach commands about revisions --- cli/src/commands/abandon.rs | 4 +++- cli/src/commands/backout.rs | 14 +++++++++-- cli/src/commands/describe.rs | 4 +++- cli/src/commands/diff.rs | 8 ++++--- cli/src/commands/diffedit.rs | 8 ++++--- cli/src/commands/duplicate.rs | 12 ++++++---- cli/src/commands/edit.rs | 3 +++ cli/src/commands/evolog.rs | 8 ++++++- cli/src/commands/file/annotate.rs | 4 +++- cli/src/commands/file/chmod.rs | 8 ++++++- cli/src/commands/file/list.rs | 8 ++++++- cli/src/commands/file/show.rs | 8 ++++++- cli/src/commands/fix.rs | 4 +++- cli/src/commands/interdiff.rs | 6 +++-- cli/src/commands/log.rs | 4 +++- cli/src/commands/move.rs | 6 +++-- cli/src/commands/new.rs | 10 +++++--- cli/src/commands/parallelize.rs | 3 +++ cli/src/commands/rebase.rs | 16 ++++++++----- cli/src/commands/resolve.rs | 8 ++++++- cli/src/commands/restore.rs | 13 +++++++--- cli/src/commands/show.rs | 4 +++- cli/src/commands/simplify_parents.rs | 14 +++++++++-- cli/src/commands/split.rs | 8 ++++++- cli/src/commands/squash.rs | 17 ++++++++++--- cli/src/complete.rs | 33 +++++++++++++++++++++++++ cli/tests/test_completion.rs | 36 ++++++++++++++++++++++++++++ 27 files changed, 226 insertions(+), 45 deletions(-) diff --git a/cli/src/commands/abandon.rs b/cli/src/commands/abandon.rs index 3c9c179d6f..77f2ed8786 100644 --- a/cli/src/commands/abandon.rs +++ b/cli/src/commands/abandon.rs @@ -14,6 +14,7 @@ use std::io::Write; +use clap_complete::ArgValueCandidates; use itertools::Itertools as _; use jj_lib::commit::CommitIteratorExt; use jj_lib::object_id::ObjectId; @@ -22,6 +23,7 @@ use tracing::instrument; use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Abandon a revision @@ -35,7 +37,7 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] pub(crate) struct AbandonArgs { /// The revision(s) to abandon - #[arg(default_value = "@")] + #[arg(default_value = "@", add = ArgValueCandidates::new(complete::mutable_revisions))] revisions: Vec, /// Do not print every abandoned commit on a separate line #[arg(long, short)] diff --git a/cli/src/commands/backout.rs b/cli/src/commands/backout.rs index 452c21eebe..5bdee8c3c9 100644 --- a/cli/src/commands/backout.rs +++ b/cli/src/commands/backout.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 as _; use jj_lib::object_id::ObjectId; use jj_lib::rewrite::merge_commit_trees; @@ -20,18 +21,27 @@ use tracing::instrument; use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Apply the reverse of a revision on top of another revision #[derive(clap::Args, Clone, Debug)] pub(crate) struct BackoutArgs { /// The revision(s) to apply the reverse of - #[arg(long, short, default_value = "@")] + #[arg( + long, short, + default_value = "@", + add = ArgValueCandidates::new(complete::all_revisions), + )] revisions: Vec, /// The revision to apply the reverse changes on top of // TODO: It seems better to default this to `@-`. Maybe the working // copy should be rebased on top? - #[arg(long, short, default_value = "@")] + #[arg( + long, short, + default_value = "@", + add = ArgValueCandidates::new(complete::all_revisions), + )] destination: Vec, } diff --git a/cli/src/commands/describe.rs b/cli/src/commands/describe.rs index 3ca4be5e90..94da607179 100644 --- a/cli/src/commands/describe.rs +++ b/cli/src/commands/describe.rs @@ -16,6 +16,7 @@ use std::collections::HashMap; use std::io; use std::io::Read; +use clap_complete::ArgValueCandidates; use itertools::Itertools; use jj_lib::backend::Signature; use jj_lib::commit::CommitIteratorExt; @@ -26,6 +27,7 @@ use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::user_error; use crate::command_error::CommandError; +use crate::complete; use crate::description_util::description_template; use crate::description_util::edit_description; use crate::description_util::edit_multiple_descriptions; @@ -42,7 +44,7 @@ use crate::ui::Ui; #[command(alias = "desc")] pub(crate) struct DescribeArgs { /// The revision(s) whose description to edit - #[arg(default_value = "@")] + #[arg(default_value = "@", add = ArgValueCandidates::new(complete::mutable_revisions))] revisions: Vec, /// Ignored (but lets you pass `-r` for consistency with other commands) #[arg(short = 'r', hide = true, action = clap::ArgAction::Count)] diff --git a/cli/src/commands/diff.rs b/cli/src/commands/diff.rs index 7394f5f148..244cc4f4c6 100644 --- a/cli/src/commands/diff.rs +++ b/cli/src/commands/diff.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::copies::CopyRecords; use jj_lib::repo::Repo; @@ -22,6 +23,7 @@ use crate::cli_util::print_unmatched_explicit_paths; use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; +use crate::complete; use crate::diff_util::get_copy_records; use crate::diff_util::DiffFormatArgs; use crate::ui::Ui; @@ -46,13 +48,13 @@ pub(crate) struct DiffArgs { /// If the revision is a merge commit, this shows changes *from* the /// automatic merge of the contents of all of its parents *to* the contents /// of the revision itself. - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::all_revisions))] revision: Option, /// Show changes from this revision - #[arg(long, conflicts_with = "revision")] + #[arg(long, conflicts_with = "revision", add = ArgValueCandidates::new(complete::all_revisions))] from: Option, /// Show changes to this revision - #[arg(long, conflicts_with = "revision")] + #[arg(long, conflicts_with = "revision", add = ArgValueCandidates::new(complete::all_revisions))] to: Option, /// Restrict the diff to these paths #[arg(value_hint = clap::ValueHint::AnyPath)] diff --git a/cli/src/commands/diffedit.rs b/cli/src/commands/diffedit.rs index 08211bce90..75a4fabf57 100644 --- a/cli/src/commands/diffedit.rs +++ b/cli/src/commands/diffedit.rs @@ -14,6 +14,7 @@ use std::io::Write; +use clap_complete::ArgValueCandidates; use itertools::Itertools; use jj_lib::matchers::EverythingMatcher; use jj_lib::object_id::ObjectId; @@ -23,6 +24,7 @@ use tracing::instrument; use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Touch up the content changes in a revision with a diff editor @@ -48,17 +50,17 @@ pub(crate) struct DiffeditArgs { /// The revision to touch up /// /// Defaults to @ if neither --to nor --from are specified. - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::mutable_revisions))] revision: Option, /// Show changes from this revision /// /// Defaults to @ if --to is specified. - #[arg(long, conflicts_with = "revision")] + #[arg(long, conflicts_with = "revision", add = ArgValueCandidates::new(complete::all_revisions))] from: Option, /// Edit changes in this revision /// /// Defaults to @ if --from is specified. - #[arg(long, conflicts_with = "revision")] + #[arg(long, conflicts_with = "revision", add = ArgValueCandidates::new(complete::mutable_revisions))] to: Option, /// Specify diff editor to be used #[arg(long, value_name = "NAME")] diff --git a/cli/src/commands/duplicate.rs b/cli/src/commands/duplicate.rs index 96f77815c6..3c7438716d 100644 --- a/cli/src/commands/duplicate.rs +++ b/cli/src/commands/duplicate.rs @@ -15,6 +15,7 @@ use std::io::Write; use std::rc::Rc; +use clap_complete::ArgValueCandidates; use itertools::Itertools; use jj_lib::backend::CommitId; use jj_lib::commit::CommitIteratorExt; @@ -32,6 +33,7 @@ use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::user_error; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Create new changes with the same content as existing ones @@ -50,14 +52,14 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] pub(crate) struct DuplicateArgs { /// The revision(s) to duplicate - #[arg(default_value = "@")] + #[arg(default_value = "@", add = ArgValueCandidates::new(complete::all_revisions))] revisions: Vec, /// Ignored (but lets you pass `-r` for consistency with other commands) #[arg(short = 'r', hide = true, action = clap::ArgAction::Count)] unused_revision: u8, /// The revision(s) to duplicate onto (can be repeated to create a merge /// commit) - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::all_revisions))] destination: Vec, /// The revision(s) to insert after (can be repeated to create a merge /// commit) @@ -65,7 +67,8 @@ pub(crate) struct DuplicateArgs { long, short = 'A', visible_alias = "after", - conflicts_with = "destination" + conflicts_with = "destination", + add = ArgValueCandidates::new(complete::all_revisions), )] insert_after: Vec, /// The revision(s) to insert before (can be repeated to create a merge @@ -74,7 +77,8 @@ pub(crate) struct DuplicateArgs { long, short = 'B', visible_alias = "before", - conflicts_with = "destination" + conflicts_with = "destination", + add = ArgValueCandidates::new(complete::mutable_revisions) )] insert_before: Vec, } diff --git a/cli/src/commands/edit.rs b/cli/src/commands/edit.rs index 84fbada998..e32ae969ac 100644 --- a/cli/src/commands/edit.rs +++ b/cli/src/commands/edit.rs @@ -14,12 +14,14 @@ use std::io::Write; +use clap_complete::ArgValueCandidates; use jj_lib::object_id::ObjectId; use tracing::instrument; use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Sets the specified revision as the working-copy revision @@ -31,6 +33,7 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] pub(crate) struct EditArgs { /// The commit to edit + #[arg(add = ArgValueCandidates::new(complete::mutable_revisions))] revision: RevisionArg, /// Ignored (but lets you pass `-r` for consistency with other commands) #[arg(short = 'r', hide = true)] diff --git a/cli/src/commands/evolog.rs b/cli/src/commands/evolog.rs index 0f40fa1610..b9bb6c3c86 100644 --- a/cli/src/commands/evolog.rs +++ b/cli/src/commands/evolog.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::commit::Commit; use jj_lib::dag_walk::topo_order_reverse_ok; @@ -25,6 +26,7 @@ use crate::cli_util::LogContentFormat; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; use crate::commit_templater::CommitTemplateLanguage; +use crate::complete; use crate::diff_util::DiffFormatArgs; use crate::graphlog::get_graphlog; use crate::graphlog::Edge; @@ -37,7 +39,11 @@ use crate::ui::Ui; /// of a change evolves when the change is updated, rebased, etc. #[derive(clap::Args, Clone, Debug)] pub(crate) struct EvologArgs { - #[arg(long, short, default_value = "@")] + #[arg( + long, short, + default_value = "@", + add = ArgValueCandidates::new(complete::all_revisions), + )] revision: RevisionArg, /// Limit number of revisions to show #[arg(long, short = 'n')] diff --git a/cli/src/commands/file/annotate.rs b/cli/src/commands/file/annotate.rs index be0b1c2a63..3ea91271f9 100644 --- a/cli/src/commands/file/annotate.rs +++ b/cli/src/commands/file/annotate.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use clap_complete::ArgValueCandidates; use jj_lib::annotate::get_annotation_for_file; use jj_lib::annotate::FileAnnotation; use jj_lib::commit::Commit; @@ -23,6 +24,7 @@ use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::user_error; use crate::command_error::CommandError; +use crate::complete; use crate::templater::TemplateRenderer; use crate::ui::Ui; @@ -38,7 +40,7 @@ pub(crate) struct FileAnnotateArgs { #[arg(value_hint = clap::ValueHint::AnyPath)] path: String, /// an optional revision to start at - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::all_revisions))] revision: Option, } diff --git a/cli/src/commands/file/chmod.rs b/cli/src/commands/file/chmod.rs index 9f90a282fb..19b6068cde 100644 --- a/cli/src/commands/file/chmod.rs +++ b/cli/src/commands/file/chmod.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use clap_complete::ArgValueCandidates; use jj_lib::backend::TreeValue; use jj_lib::merged_tree::MergedTreeBuilder; use jj_lib::object_id::ObjectId; @@ -22,6 +23,7 @@ use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::user_error; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] @@ -43,7 +45,11 @@ enum ChmodMode { pub(crate) struct FileChmodArgs { mode: ChmodMode, /// The revision to update - #[arg(long, short, default_value = "@")] + #[arg( + long, short, + default_value = "@", + add = ArgValueCandidates::new(complete::mutable_revisions), + )] revision: RevisionArg, /// Paths to change the executable bit for #[arg(required = true, value_hint = clap::ValueHint::AnyPath)] diff --git a/cli/src/commands/file/list.rs b/cli/src/commands/file/list.rs index 8c0268b224..202fda788c 100644 --- a/cli/src/commands/file/list.rs +++ b/cli/src/commands/file/list.rs @@ -14,18 +14,24 @@ use std::io::Write; +use clap_complete::ArgValueCandidates; use tracing::instrument; use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// List files in a revision #[derive(clap::Args, Clone, Debug)] pub(crate) struct FileListArgs { /// The revision to list files in - #[arg(long, short, default_value = "@")] + #[arg( + long, short, + default_value = "@", + add = ArgValueCandidates::new(complete::all_revisions), + )] revision: RevisionArg, /// Only list files matching these prefixes (instead of all files) #[arg(value_hint = clap::ValueHint::AnyPath)] diff --git a/cli/src/commands/file/show.rs b/cli/src/commands/file/show.rs index 14482bc3df..98619bb5e3 100644 --- a/cli/src/commands/file/show.rs +++ b/cli/src/commands/file/show.rs @@ -15,6 +15,7 @@ use std::io; use std::io::Write; +use clap_complete::ArgValueCandidates; use jj_lib::backend::BackendResult; use jj_lib::conflicts::materialize_merge_result; use jj_lib::conflicts::materialize_tree_value; @@ -33,6 +34,7 @@ use crate::cli_util::RevisionArg; use crate::cli_util::WorkspaceCommandHelper; use crate::command_error::user_error; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Print contents of files in a revision @@ -42,7 +44,11 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] pub(crate) struct FileShowArgs { /// The revision to get the file contents from - #[arg(long, short, default_value = "@")] + #[arg( + long, short, + default_value = "@", + add = ArgValueCandidates::new(complete::all_revisions), + )] revision: RevisionArg, /// Paths to print #[arg(required = true, value_hint = clap::ValueHint::FilePath)] diff --git a/cli/src/commands/fix.rs b/cli/src/commands/fix.rs index 3677074a44..71fabc8292 100644 --- a/cli/src/commands/fix.rs +++ b/cli/src/commands/fix.rs @@ -18,6 +18,7 @@ use std::io::Write; use std::process::Stdio; use std::sync::mpsc::channel; +use clap_complete::ArgValueCandidates; use futures::StreamExt; use itertools::Itertools; use jj_lib::backend::BackendError; @@ -49,6 +50,7 @@ use crate::cli_util::RevisionArg; use crate::command_error::config_error; use crate::command_error::print_parse_diagnostics; use crate::command_error::CommandError; +use crate::complete; use crate::config::to_toml_value; use crate::config::CommandNameAndArgs; use crate::ui::Ui; @@ -124,7 +126,7 @@ pub(crate) struct FixArgs { /// Fix files in the specified revision(s) and their descendants. If no /// revisions are specified, this defaults to the `revsets.fix` setting, or /// `reachable(@, mutable())` if it is not set. - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::mutable_revisions))] source: Vec, /// Fix only these paths #[arg(value_hint = clap::ValueHint::AnyPath)] diff --git a/cli/src/commands/interdiff.rs b/cli/src/commands/interdiff.rs index b8d5cd487f..75bc9439ab 100644 --- a/cli/src/commands/interdiff.rs +++ b/cli/src/commands/interdiff.rs @@ -15,11 +15,13 @@ use std::slice; use clap::ArgGroup; +use clap_complete::ArgValueCandidates; use tracing::instrument; use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; +use crate::complete; use crate::diff_util::DiffFormatArgs; use crate::ui::Ui; @@ -34,10 +36,10 @@ use crate::ui::Ui; #[command(mut_arg("ignore_space_change", |a| a.short('b')))] pub(crate) struct InterdiffArgs { /// Show changes from this revision - #[arg(long)] + #[arg(long, add = ArgValueCandidates::new(complete::all_revisions))] from: Option, /// Show changes to this revision - #[arg(long)] + #[arg(long, add = ArgValueCandidates::new(complete::all_revisions))] to: Option, /// Restrict the diff to these paths #[arg(value_hint = clap::ValueHint::AnyPath)] diff --git a/cli/src/commands/log.rs b/cli/src/commands/log.rs index e3a04c3239..5f1b2a8e3b 100644 --- a/cli/src/commands/log.rs +++ b/cli/src/commands/log.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use clap_complete::ArgValueCandidates; use jj_lib::backend::CommitId; use jj_lib::graph::GraphEdgeType; use jj_lib::graph::ReverseGraphIterator; @@ -31,6 +32,7 @@ use crate::cli_util::LogContentFormat; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; use crate::commit_templater::CommitTemplateLanguage; +use crate::complete; use crate::diff_util::DiffFormatArgs; use crate::graphlog::get_graphlog; use crate::graphlog::Edge; @@ -51,7 +53,7 @@ pub(crate) struct LogArgs { /// /// If no paths nor revisions are specified, this defaults to the /// `revsets.log` setting. - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::all_revisions))] revisions: Vec, /// Show revisions modifying the given paths #[arg(value_hint = clap::ValueHint::AnyPath)] diff --git a/cli/src/commands/move.rs b/cli/src/commands/move.rs index 157ee4c16c..236dc09e11 100644 --- a/cli/src/commands/move.rs +++ b/cli/src/commands/move.rs @@ -13,6 +13,7 @@ // limitations under the License. use clap::ArgGroup; +use clap_complete::ArgValueCandidates; use jj_lib::object_id::ObjectId; use tracing::instrument; @@ -22,6 +23,7 @@ use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::user_error; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Move changes from one revision into another (DEPRECATED, use `jj squash`) @@ -43,10 +45,10 @@ use crate::ui::Ui; #[command(group(ArgGroup::new("to_move").args(&["from", "to"]).multiple(true).required(true)))] pub(crate) struct MoveArgs { /// Move part of this change into the destination - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::mutable_revisions))] from: Option, /// Move part of the source into this change - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::mutable_revisions))] to: Option, /// Interactively choose which parts to move #[arg(long, short)] diff --git a/cli/src/commands/new.rs b/cli/src/commands/new.rs index e4a70a4dc7..09f7575b36 100644 --- a/cli/src/commands/new.rs +++ b/cli/src/commands/new.rs @@ -16,6 +16,7 @@ use std::collections::HashSet; use std::io::Write; use std::rc::Rc; +use clap_complete::ArgValueCandidates; use itertools::Itertools; use jj_lib::backend::CommitId; use jj_lib::commit::CommitIteratorExt; @@ -33,6 +34,7 @@ use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::user_error; use crate::command_error::CommandError; +use crate::complete; use crate::description_util::join_message_paragraphs; use crate::ui::Ui; @@ -50,7 +52,7 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] pub(crate) struct NewArgs { /// Parent(s) of the new change - #[arg(default_value = "@")] + #[arg(default_value = "@", add = ArgValueCandidates::new(complete::all_revisions))] pub(crate) revisions: Vec, /// Ignored (but lets you pass `-d`/`-r` for consistency with other /// commands) @@ -70,7 +72,8 @@ pub(crate) struct NewArgs { long, short = 'A', visible_alias = "after", - conflicts_with = "revisions" + conflicts_with = "revisions", + add = ArgValueCandidates::new(complete::all_revisions), )] insert_after: Vec, /// Insert the new change before the given commit(s) @@ -78,7 +81,8 @@ pub(crate) struct NewArgs { long, short = 'B', visible_alias = "before", - conflicts_with = "revisions" + conflicts_with = "revisions", + add = ArgValueCandidates::new(complete::mutable_revisions), )] insert_before: Vec, } diff --git a/cli/src/commands/parallelize.rs b/cli/src/commands/parallelize.rs index 3971e0c82a..e252c60f68 100644 --- a/cli/src/commands/parallelize.rs +++ b/cli/src/commands/parallelize.rs @@ -14,6 +14,7 @@ use std::collections::HashMap; +use clap_complete::ArgValueCandidates; use indexmap::IndexSet; use itertools::Itertools; use jj_lib::backend::CommitId; @@ -24,6 +25,7 @@ use tracing::instrument; use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Parallelize revisions by making them siblings @@ -55,6 +57,7 @@ use crate::ui::Ui; #[command(verbatim_doc_comment)] pub(crate) struct ParallelizeArgs { /// Revisions to parallelize + #[arg(add = ArgValueCandidates::new(complete::mutable_revisions))] revisions: Vec, } diff --git a/cli/src/commands/rebase.rs b/cli/src/commands/rebase.rs index 2526ac73f4..1d0a3817fc 100644 --- a/cli/src/commands/rebase.rs +++ b/cli/src/commands/rebase.rs @@ -17,6 +17,7 @@ use std::rc::Rc; use std::sync::Arc; use clap::ArgGroup; +use clap_complete::ArgValueCandidates; use itertools::Itertools; use jj_lib::backend::CommitId; use jj_lib::commit::Commit; @@ -42,6 +43,7 @@ use crate::cli_util::WorkspaceCommandHelper; use crate::command_error::cli_error; use crate::command_error::user_error; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Move revisions to different parent(s) @@ -140,7 +142,7 @@ pub(crate) struct RebaseArgs { /// -d=dst`. /// /// If none of `-b`, `-s`, or `-r` is provided, then the default is `-b @`. - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::mutable_revisions))] branch: Vec, /// Rebase specified revision(s) together with their trees of descendants @@ -151,7 +153,7 @@ pub(crate) struct RebaseArgs { /// of others. /// /// If none of `-b`, `-s`, or `-r` is provided, then the default is `-b @`. - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::mutable_revisions))] source: Vec, /// Rebase the given revisions, rebasing descendants onto this revision's /// parent(s) @@ -160,7 +162,7 @@ pub(crate) struct RebaseArgs { /// descendant of `A`. /// /// If none of `-b`, `-s`, or `-r` is provided, then the default is `-b @`. - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::mutable_revisions))] revisions: Vec, #[command(flatten)] @@ -183,7 +185,7 @@ pub(crate) struct RebaseArgs { pub struct RebaseDestinationArgs { /// The revision(s) to rebase onto (can be repeated to create a merge /// commit) - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::all_revisions))] destination: Option>, /// The revision(s) to insert after (can be repeated to create a merge /// commit) @@ -191,7 +193,8 @@ pub struct RebaseDestinationArgs { long, short = 'A', visible_alias = "after", - conflicts_with = "destination" + conflicts_with = "destination", + add = ArgValueCandidates::new(complete::all_revisions), )] insert_after: Option>, /// The revision(s) to insert before (can be repeated to create a merge @@ -200,7 +203,8 @@ pub struct RebaseDestinationArgs { long, short = 'B', visible_alias = "before", - conflicts_with = "destination" + conflicts_with = "destination", + add = ArgValueCandidates::new(complete::mutable_revisions), )] insert_before: Option>, } diff --git a/cli/src/commands/resolve.rs b/cli/src/commands/resolve.rs index 9cce2cd588..a03a2f8b8d 100644 --- a/cli/src/commands/resolve.rs +++ b/cli/src/commands/resolve.rs @@ -14,6 +14,7 @@ use std::io::Write; +use clap_complete::ArgValueCandidates; use itertools::Itertools; use jj_lib::object_id::ObjectId; use tracing::instrument; @@ -23,6 +24,7 @@ use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::cli_error; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Resolve a conflicted file with an external merge tool @@ -42,7 +44,11 @@ use crate::ui::Ui; // simplify the present one. #[derive(clap::Args, Clone, Debug)] pub(crate) struct ResolveArgs { - #[arg(long, short, default_value = "@")] + #[arg( + long, short, + default_value = "@", + add = ArgValueCandidates::new(complete::mutable_revisions), + )] revision: RevisionArg, /// Instead of resolving one conflict, list all the conflicts // TODO: Also have a `--summary` option. `--list` currently acts like diff --git a/cli/src/commands/restore.rs b/cli/src/commands/restore.rs index 6965c91350..42a68c5257 100644 --- a/cli/src/commands/restore.rs +++ b/cli/src/commands/restore.rs @@ -14,6 +14,7 @@ use std::io::Write; +use clap_complete::ArgValueCandidates; use jj_lib::object_id::ObjectId; use jj_lib::rewrite::restore_tree; use tracing::instrument; @@ -22,6 +23,7 @@ use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::user_error; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Restore paths from another revision @@ -46,10 +48,10 @@ pub(crate) struct RestoreArgs { #[arg(value_hint = clap::ValueHint::AnyPath)] paths: Vec, /// Revision to restore from (source) - #[arg(long)] + #[arg(long, add = ArgValueCandidates::new(complete::all_revisions))] from: Option, /// Revision to restore into (destination) - #[arg(long)] + #[arg(long, add = ArgValueCandidates::new(complete::mutable_revisions))] to: Option, /// Undo the changes in a revision as compared to the merge of its parents. /// @@ -59,7 +61,12 @@ pub(crate) struct RestoreArgs { /// /// The default behavior of `jj restore` is equivalent to `jj restore /// --changes-in @`. - #[arg(long, short, value_name="REVISION", conflicts_with_all=["to", "from"])] + #[arg( + long, short, + value_name = "REVISION", + conflicts_with_all = ["to", "from"], + add = ArgValueCandidates::new(complete::all_revisions), + )] changes_in: Option, /// Prints an error. DO NOT USE. /// diff --git a/cli/src/commands/show.rs b/cli/src/commands/show.rs index e363d66b7e..ad7e21bad9 100644 --- a/cli/src/commands/show.rs +++ b/cli/src/commands/show.rs @@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +use clap_complete::ArgValueCandidates; use jj_lib::matchers::EverythingMatcher; use tracing::instrument; use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; +use crate::complete; use crate::diff_util::DiffFormatArgs; use crate::ui::Ui; @@ -25,7 +27,7 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] pub(crate) struct ShowArgs { /// Show changes in this revision, compared to its parent(s) - #[arg(default_value = "@")] + #[arg(default_value = "@", add = ArgValueCandidates::new(complete::all_revisions))] revision: RevisionArg, /// Ignored (but lets you pass `-r` for consistency with other commands) #[arg(short = 'r', hide = true)] diff --git a/cli/src/commands/simplify_parents.rs b/cli/src/commands/simplify_parents.rs index 5700344a97..78a105db52 100644 --- a/cli/src/commands/simplify_parents.rs +++ b/cli/src/commands/simplify_parents.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use clap_complete::ArgValueCandidates; use itertools::Itertools; use jj_lib::backend::BackendResult; use jj_lib::revset::RevsetExpression; @@ -9,6 +10,7 @@ use jj_lib::settings::UserSettings; use crate::cli_util::CommandHelper; use crate::cli_util::RevisionArg; use crate::command_error::CommandError; +use crate::complete; use crate::ui::Ui; /// Simplify parent edges for the specified revision(s). @@ -25,10 +27,18 @@ use crate::ui::Ui; pub(crate) struct SimplifyParentsArgs { /// Simplify specified revision(s) together with their trees of descendants /// (can be repeated) - #[arg(long, short, group = "revision-args")] + #[arg( + long, short, + group = "revision-args", + add = ArgValueCandidates::new(complete::mutable_revisions), + )] source: Vec, /// Simplify specified revision(s) (can be repeated) - #[arg(long, short, group = "revision-args")] + #[arg( + long, short, + group = "revision-args", + add = ArgValueCandidates::new(complete::mutable_revisions), + )] revisions: Vec, } diff --git a/cli/src/commands/split.rs b/cli/src/commands/split.rs index 80bdae5105..2ce219b303 100644 --- a/cli/src/commands/split.rs +++ b/cli/src/commands/split.rs @@ -13,6 +13,7 @@ // limitations under the License. use std::io::Write; +use clap_complete::ArgValueCandidates; use jj_lib::object_id::ObjectId; use jj_lib::repo::Repo; use tracing::instrument; @@ -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::description_util::description_template; use crate::description_util::edit_description; use crate::ui::Ui; @@ -52,7 +54,11 @@ pub(crate) struct SplitArgs { #[arg(long, value_name = "NAME")] tool: Option, /// The revision to split - #[arg(long, short, default_value = "@")] + #[arg( + long, short, + default_value = "@", + add = ArgValueCandidates::new(complete::mutable_revisions) + )] revision: RevisionArg, /// Split the revision into two parallel revisions instead of a parent and /// child. diff --git a/cli/src/commands/squash.rs b/cli/src/commands/squash.rs index 2c77253a5d..5baeb6dcbd 100644 --- a/cli/src/commands/squash.rs +++ b/cli/src/commands/squash.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 as _; use jj_lib::commit::Commit; use jj_lib::commit::CommitIteratorExt; @@ -29,6 +30,7 @@ use crate::cli_util::WorkspaceCommandTransaction; use crate::command_error::user_error; use crate::command_error::user_error_with_hint; use crate::command_error::CommandError; +use crate::complete; use crate::description_util::combine_messages; use crate::description_util::join_message_paragraphs; use crate::ui::Ui; @@ -57,13 +59,22 @@ use crate::ui::Ui; #[derive(clap::Args, Clone, Debug)] pub(crate) struct SquashArgs { /// Revision to squash into its parent (default: @) - #[arg(long, short)] + #[arg(long, short, add = ArgValueCandidates::new(complete::mutable_revisions))] revision: Option, /// Revision(s) to squash from (default: @) - #[arg(long, short, conflicts_with = "revision")] + #[arg( + long, short, + conflicts_with = "revision", + add = ArgValueCandidates::new(complete::mutable_revisions), + )] from: Vec, /// Revision to squash into (default: @) - #[arg(long, short = 't', conflicts_with = "revision", visible_alias = "to")] + #[arg( + long, short = 't', + conflicts_with = "revision", + visible_alias = "to", + add = ArgValueCandidates::new(complete::mutable_revisions), + )] into: Option, /// The description to use for squashed revision (don't open editor) #[arg(long = "message", short, value_name = "MESSAGE")] diff --git a/cli/src/complete.rs b/cli/src/complete.rs index 3c41ee1b52..1f7dd94e98 100644 --- a/cli/src/complete.rs +++ b/cli/src/complete.rs @@ -208,6 +208,39 @@ pub fn aliases() -> Vec { }) } +fn revisions(revisions: &str) -> Vec { + with_jj(|mut jj, _| { + let output = jj + .arg("log") + .arg("--no-graph") + .arg("--limit") + .arg("100") + .arg("--revisions") + .arg(revisions) + .arg("--template") + .arg(r#"change_id.shortest() ++ " " ++ if(description, description.first_line(), "(no description set)") ++ "\n""#) + .output() + .map_err(user_error)?; + let stdout = String::from_utf8_lossy(&output.stdout); + + Ok(stdout + .lines() + .map(|line| { + let (id, desc) = split_help_text(line); + CompletionCandidate::new(id).help(desc) + }) + .collect()) + }) +} + +pub fn mutable_revisions() -> Vec { + revisions("mutable()") +} + +pub fn all_revisions() -> Vec { + revisions("all()") +} + /// 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 54b5cdf6e2..59f7e40ec7 100644 --- a/cli/tests/test_completion.rs +++ b/cli/tests/test_completion.rs @@ -309,3 +309,39 @@ fn test_aliases_are_completed() { ); insta::assert_snapshot!(stdout, @""); } + +#[test] +fn test_revisions() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]); + let repo_path = test_env.env_root().join("repo"); + + test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "immutable"]); + test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "mutable"]); + test_env.jj_cmd_ok(&repo_path, &["bookmark", "create", "main", "-r", "@--"]); + test_env.add_config(r#"revset-aliases."immutable_heads()" = "main""#); + + let mut test_env = test_env; + test_env.add_env_var("COMPLETE", "fish"); + let test_env = test_env; + + // There are _a lot_ of commands and arguments accepting revisions. + // Let's not test all of them. Having at least one test per variation of + // completion function should be sufficient. + + // complete all revisions + let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "diff", "--from", ""]); + insta::assert_snapshot!(stdout, @r" + k (no description set) + r mutable + q immutable + z (no description set) + "); + + // complete only mutable revisions + let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "squash", "--into", ""]); + insta::assert_snapshot!(stdout, @r" + k (no description set) + r mutable + "); +}