mirror of
https://github.com/martinvonz/jj.git
synced 2024-11-24 06:19:42 +00:00
completion: teach log about files
Some checks failed
build / build (, macos-13) (push) Has been cancelled
build / build (, macos-14) (push) Has been cancelled
build / build (, ubuntu-latest) (push) Has been cancelled
build / build (, windows-latest) (push) Has been cancelled
build / build (--all-features, ubuntu-latest) (push) Has been cancelled
build / Build jj-lib without Git support (push) Has been cancelled
build / Check protos (push) Has been cancelled
build / Check formatting (push) Has been cancelled
build / Check that MkDocs can build the docs (push) Has been cancelled
build / Check that MkDocs can build the docs with Poetry 1.8 (push) Has been cancelled
build / cargo-deny (advisories) (push) Has been cancelled
build / cargo-deny (bans licenses sources) (push) Has been cancelled
build / Clippy check (push) Has been cancelled
Some checks failed
build / build (, macos-13) (push) Has been cancelled
build / build (, macos-14) (push) Has been cancelled
build / build (, ubuntu-latest) (push) Has been cancelled
build / build (, windows-latest) (push) Has been cancelled
build / build (--all-features, ubuntu-latest) (push) Has been cancelled
build / Build jj-lib without Git support (push) Has been cancelled
build / Check protos (push) Has been cancelled
build / Check formatting (push) Has been cancelled
build / Check that MkDocs can build the docs (push) Has been cancelled
build / Check that MkDocs can build the docs with Poetry 1.8 (push) Has been cancelled
build / cargo-deny (advisories) (push) Has been cancelled
build / cargo-deny (bans licenses sources) (push) Has been cancelled
build / Clippy check (push) Has been cancelled
This commit is contained in:
parent
ad8d9924a8
commit
26e5947820
3 changed files with 98 additions and 8 deletions
|
@ -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;
|
||||
|
@ -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)]
|
||||
|
|
|
@ -363,7 +363,7 @@ pub fn leaf_config_keys() -> Vec<CompletionCandidate> {
|
|||
/// multiple times, the parsing will pick any of the available ones, while the
|
||||
/// actual execution of the command would fail.
|
||||
mod parse {
|
||||
fn parse_flag(candidates: &[&str], mut args: impl Iterator<Item = String>) -> Option<String> {
|
||||
fn parse_flag(candidates: &[&str], args: &mut impl Iterator<Item = String>) -> Option<String> {
|
||||
for arg in args.by_ref() {
|
||||
if candidates.contains(&arg.as_ref()) {
|
||||
return args.next();
|
||||
|
@ -378,8 +378,8 @@ mod parse {
|
|||
None
|
||||
}
|
||||
|
||||
fn parse_revision_impl(args: impl Iterator<Item = String>) -> Option<String> {
|
||||
parse_flag(&["-r", "--revision"], args)
|
||||
fn parse_revision_impl(mut args: impl Iterator<Item = String>) -> Option<String> {
|
||||
parse_flag(&["-r", "--revision"], &mut args)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -419,8 +419,8 @@ mod parse {
|
|||
where
|
||||
T: Iterator<Item = String>,
|
||||
{
|
||||
let from = parse_flag(&["-f", "--from"], args())?;
|
||||
let to = parse_flag(&["-t", "--to"], args()).unwrap_or_else(|| "@".into());
|
||||
let from = parse_flag(&["-f", "--from"], &mut args())?;
|
||||
let to = parse_flag(&["-t", "--to"], &mut args()).unwrap_or_else(|| "@".into());
|
||||
|
||||
Some((from, to))
|
||||
}
|
||||
|
@ -472,10 +472,35 @@ mod parse {
|
|||
// the files changed only in some other revision in the range between
|
||||
// --from and --to cannot be squashed into --to like that.
|
||||
pub fn squash_revision() -> Option<String> {
|
||||
if let Some(rev) = parse_flag(&["-r", "--revision"], std::env::args()) {
|
||||
if let Some(rev) = parse_flag(&["-r", "--revision"], &mut std::env::args()) {
|
||||
return Some(rev);
|
||||
}
|
||||
parse_flag(&["-f", "--from"], std::env::args())
|
||||
parse_flag(&["-f", "--from"], &mut 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 union = std::iter::from_fn(|| parse_flag(candidates, &mut args))
|
||||
// multiple -r arguments are interpreted as a union
|
||||
.fold("none()".into(), |mut buf: String, rev| {
|
||||
buf.push_str("|(");
|
||||
buf.push_str(&rev);
|
||||
buf.push(')');
|
||||
buf
|
||||
});
|
||||
if union == "none()" {
|
||||
return "@".into();
|
||||
}
|
||||
union
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -641,6 +666,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.
|
||||
|
|
|
@ -723,4 +723,31 @@ fn test_files() {
|
|||
f_dir/
|
||||
f_modified
|
||||
");
|
||||
|
||||
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "log", "f_"]);
|
||||
insta::assert_snapshot!(stdout.replace('\\', "/"), @r"
|
||||
f_added_2
|
||||
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
|
||||
");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue