Skip to content

Commit

Permalink
completion: teach jj about aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
senekor committed Nov 11, 2024
1 parent 4f91bf3 commit c8ad0ea
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 2 deletions.
8 changes: 6 additions & 2 deletions cli/src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3079,8 +3079,12 @@ fn handle_shell_completion(
let resolved_aliases = expand_args(ui, app, env::args_os().skip(2), config)?;
args.extend(resolved_aliases.into_iter().map(OsString::from));
}
let ran_completion = clap_complete::CompleteEnv::with_factory(|| app.clone())
.try_complete(args.iter(), Some(cwd))?;
let ran_completion = clap_complete::CompleteEnv::with_factory(|| {
app.clone()
// for completing aliases
.allow_external_subcommands(true)
})
.try_complete(args.iter(), Some(cwd))?;
assert!(
ran_completion,
"This function should not be called without the COMPLETE variable set."
Expand Down
3 changes: 3 additions & 0 deletions cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,20 @@ use std::fmt::Debug;
use clap::CommandFactory;
use clap::FromArgMatches;
use clap::Subcommand;
use clap_complete::engine::SubcommandCandidates;
use tracing::instrument;

use crate::cli_util::Args;
use crate::cli_util::CommandHelper;
use crate::command_error::user_error_with_hint;
use crate::command_error::CommandError;
use crate::complete;
use crate::ui::Ui;

#[derive(clap::Parser, Clone, Debug)]
#[command(disable_help_subcommand = true)]
#[command(after_long_help = help::show_keyword_hint_after_help())]
#[command(add = SubcommandCandidates::new(complete::aliases))]
enum Command {
Abandon(abandon::AbandonArgs),
Backout(backout::BackoutArgs),
Expand Down
23 changes: 23 additions & 0 deletions cli/src/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ pub fn local_bookmarks() -> Vec<CompletionCandidate> {
})
}

pub fn aliases() -> Vec<CompletionCandidate> {
with_jj(|_, config| {
Ok(config
.get_table("aliases")?
.into_keys()
// This is opinionated, but many people probably have several
// single- or two-letter aliases they use all the time. These
// aliases don't need to be completed and they would only clutter
// the output of `jj <TAB>`.
.filter(|alias| alias.len() > 2)
.map(CompletionCandidate::new)
.collect())
})
}

/// Shell out to jj during dynamic completion generation
///
/// In case of errors, print them and early return an empty vector.
Expand Down Expand Up @@ -107,6 +122,14 @@ fn get_jj_command() -> Result<(std::process::Command, Config), CommandError> {
.disable_version_flag(true)
.disable_help_flag(true)
.ignore_errors(true)
// Here, allow_external_subcommands fixes a weird issue. Without it,
// parsing GlobalArgs will fail with the message that a required arg
// is missing, where the required arg is a boolean flag. This seems
// unexpected, because missing boolean flags are usually treated as
// false. It is also not clear to me why allow_external_subcommands
// changes this behavior. See the discussion in the clap repo:
// https://github.com/clap-rs/clap/discussions/5812
.allow_external_subcommands(true)
.try_get_matches_from(args)?;
let args: GlobalArgs = GlobalArgs::from_arg_matches(&args)?;

Expand Down
44 changes: 44 additions & 0 deletions cli/tests/test_completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,47 @@ 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_aliases_are_completed() {
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");

// user config alias
test_env.add_config(r#"aliases.user-alias = ["bookmark"]"#);
// repo config alias
test_env.jj_cmd_ok(
&repo_path,
&[
"config",
"set",
"--repo",
"aliases.repo-alias",
"['bookmark']",
],
);

let mut test_env = test_env;
test_env.add_env_var("COMPLETE", "fish");
let test_env = test_env;

let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "user-al"]);
insta::assert_snapshot!(stdout, @"user-alias");

let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "repo-al"]);
insta::assert_snapshot!(stdout, @"repo-alias");

// make sure --repository flag is respected
let stdout = test_env.jj_cmd_success(
test_env.env_root(),
&[
"--",
"jj",
"--repository",
repo_path.to_str().unwrap(),
"repo-al",
],
);
insta::assert_snapshot!(stdout, @"repo-alias");
}

0 comments on commit c8ad0ea

Please sign in to comment.