Skip to content

Commit

Permalink
completion: teach log about files
Browse files Browse the repository at this point in the history
  • Loading branch information
senekor committed Nov 17, 2024
1 parent ccbcdf3 commit 5810f2f
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 1 deletion.
3 changes: 2 additions & 1 deletion cli/src/commands/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

use clap_complete::ArgValueCandidates;
use clap_complete::ArgValueCompleter;
use jj_lib::backend::CommitId;
use jj_lib::graph::GraphEdgeType;
use jj_lib::graph::ReverseGraphIterator;
Expand Down Expand Up @@ -64,7 +65,7 @@ pub(crate) struct LogArgs {
#[arg(long, short, add = ArgValueCandidates::new(complete::all_revisions))]
revisions: Vec<RevisionArg>,
/// Show revisions modifying the given paths
#[arg(value_hint = clap::ValueHint::AnyPath)]
#[arg(add = ArgValueCompleter::new(complete::log_files))]
paths: Vec<String>,
/// Show revisions in the opposite order (older revisions first)
#[arg(long)]
Expand Down
75 changes: 75 additions & 0 deletions cli/src/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,44 @@ mod parse {
}
parse_flag(&["-f", "--from"], std::env::args())
}

// Special parse function only for `jj log`. It has a --revisions flag,
// instead of the usual --revision, and it can be supplied multiple times.
// The default revset for log _with specified paths_ is 'all()', so it
// would be most "correct" to use that as the default. However, that is
// terrible for performance. Instead, we just complete the files in "@".
// If the user still wants to have completions for every file that has
// ever existed in the repository, they can still provide -r=all().
pub fn log_revision() -> String {
let candidates = &["-r", "--revisions"];
let mut args = std::env::args();
let mut revisions = Vec::new();

while let Some(arg) = args.next() {
if candidates.contains(&arg.as_ref()) {
match args.next() {
Some(rev) => revisions.push(rev),
None => continue,
}
}
let Some((flag, rev)) = arg.split_once('=') else {
continue;
};
if candidates.contains(&flag) {
revisions.push(rev.into());
}
}
if revisions.is_empty() {
return "@".into();
}
// multiple -r arguments are interpreted as a union
revisions.into_iter().fold("none()".into(), |mut buf, rev| {
buf.push_str("|(");
buf.push_str(&rev);
buf.push(')');
buf
})
}
}

fn dir_prefix_from<'a>(path: &'a str, current: &str) -> Option<&'a str> {
Expand Down Expand Up @@ -558,6 +596,43 @@ pub fn interdiff_files(current: &std::ffi::OsStr) -> Vec<CompletionCandidate> {
modified_files_from_rev((from, Some(to)), true, current)
}

/// Specific function for completing file paths for `jj log`
pub fn log_files(current: &std::ffi::OsStr) -> Vec<CompletionCandidate> {
let Some(current) = current.to_str() else {
return Vec::new();
};
let rev = parse::log_revision();
with_jj(|mut jj, _| {
let output = jj
.arg("log")
.arg("--no-graph")
.arg("--template=")
.arg("--summary")
.arg("--revisions")
.arg(rev)
.output()
.map_err(user_error)?;
let stdout = String::from_utf8_lossy(&output.stdout);

Ok(stdout
.lines()
.filter_map(|line| {
let (_mode, path) = line.split_at(2);

if !path.starts_with(current) {
return None;
}
if let Some(dir_path) = dir_prefix_from(path, current) {
return Some(CompletionCandidate::new(dir_path));
}

Some(CompletionCandidate::new(path))
})
.dedup() // directories may occur multiple times
.collect())
})
}

/// Shell out to jj during dynamic completion generation
///
/// In case of errors, print them and early return an empty vector.
Expand Down
22 changes: 22 additions & 0 deletions cli/tests/test_completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,4 +629,26 @@ fn test_files() {
f_dir/
f_modified
");

let stdout = test_env.jj_cmd_success(
&repo_path,
&[
"--",
"jj",
"log",
"-r=first",
"--revisions",
"conflicted",
"f_",
],
);
insta::assert_snapshot!(stdout.replace('\\', "/"), @r"
f_added_2
f_dir/
f_modified
f_deleted
f_modified
f_not_yet_renamed
f_unchanged
");
}

0 comments on commit 5810f2f

Please sign in to comment.