completion: add help text for bookmark names

This commit is contained in:
Remo Senekowitsch 2024-11-14 17:46:09 +01:00
parent 9d31e74992
commit 68c0fd6350
2 changed files with 87 additions and 38 deletions

View file

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use clap::builder::StyledStr;
use clap::FromArgMatches as _;
use clap_complete::CompletionCandidate;
use config::Config;
@ -28,19 +29,45 @@ use crate::config::default_config;
use crate::config::LayeredConfigs;
use crate::ui::Ui;
const BOOKMARK_HELP_TEMPLATE: &str = r#"
[template-aliases]
"bookmark_help()" = """
" " ++
if(normal_target,
if(normal_target.description(),
normal_target.description().first_line(),
"(no description set)",
),
"(conflicted bookmark)",
)
"""
"#;
/// A helper function for various completer functions. It returns
/// (candidate, help) assuming they are separated by a space.
fn split_help_text(line: &str) -> (&str, Option<StyledStr>) {
match line.split_once(' ') {
Some((name, help)) => (name, Some(help.to_string().into())),
None => (line, None),
}
}
pub fn local_bookmarks() -> Vec<CompletionCandidate> {
with_jj(|mut jj, _| {
let output = jj
.arg("bookmark")
.arg("list")
.arg("--config-toml")
.arg(BOOKMARK_HELP_TEMPLATE)
.arg("--template")
.arg(r#"if(!remote, name ++ "\n")"#)
.arg(r#"if(!remote, name ++ bookmark_help()) ++ "\n""#)
.output()
.map_err(user_error)?;
Ok(String::from_utf8_lossy(&output.stdout)
.lines()
.map(CompletionCandidate::new)
.map(split_help_text)
.map(|(name, help)| CompletionCandidate::new(name).help(help))
.collect())
})
}
@ -51,14 +78,17 @@ pub fn tracked_bookmarks() -> Vec<CompletionCandidate> {
.arg("bookmark")
.arg("list")
.arg("--tracked")
.arg("--config-toml")
.arg(BOOKMARK_HELP_TEMPLATE)
.arg("--template")
.arg(r#"if(remote, name ++ "@" ++ remote ++ "\n")"#)
.arg(r#"if(remote, name ++ '@' ++ remote ++ bookmark_help() ++ "\n")"#)
.output()
.map_err(user_error)?;
Ok(String::from_utf8_lossy(&output.stdout)
.lines()
.map(CompletionCandidate::new)
.map(split_help_text)
.map(|(name, help)| CompletionCandidate::new(name).help(help))
.collect())
})
}
@ -69,8 +99,14 @@ pub fn untracked_bookmarks() -> Vec<CompletionCandidate> {
.arg("bookmark")
.arg("list")
.arg("--all-remotes")
.arg("--config-toml")
.arg(BOOKMARK_HELP_TEMPLATE)
.arg("--template")
.arg(r#"if(remote && !tracked, name ++ "@" ++ remote ++ "\n")"#)
.arg(
r#"if(remote && !tracked && remote != "git",
name ++ '@' ++ remote ++ bookmark_help() ++ "\n"
)"#,
)
.output()
.map_err(user_error)?;
@ -78,14 +114,17 @@ pub fn untracked_bookmarks() -> Vec<CompletionCandidate> {
Ok(String::from_utf8_lossy(&output.stdout)
.lines()
.filter(|bookmark| !bookmark.ends_with("@git"))
.map(|bookmark| {
.map(|line| {
let (name, help) = split_help_text(line);
let display_order = match prefix.as_ref() {
// own bookmarks are more interesting
Some(prefix) if bookmark.starts_with(prefix) => 0,
Some(prefix) if name.starts_with(prefix) => 0,
_ => 1,
};
CompletionCandidate::new(bookmark).display_order(Some(display_order))
CompletionCandidate::new(name)
.help(help)
.display_order(Some(display_order))
})
.collect())
})
@ -97,8 +136,13 @@ pub fn bookmarks() -> Vec<CompletionCandidate> {
.arg("bookmark")
.arg("list")
.arg("--all-remotes")
.arg("--config-toml")
.arg(BOOKMARK_HELP_TEMPLATE)
.arg("--template")
.arg(r#"separate("@", name, remote) ++ "\n""#)
.arg(
// only provide help for local refs, remote could be ambiguous
r#"name ++ if(remote, "@" ++ remote, bookmark_help()) ++ "\n""#,
)
.output()
.map_err(user_error)?;
let stdout = String::from_utf8_lossy(&output.stdout);
@ -107,10 +151,13 @@ pub fn bookmarks() -> Vec<CompletionCandidate> {
Ok((&stdout
.lines()
.chunk_by(|line| line.split_once('@').map(|t| t.0).unwrap_or(line)))
.map(split_help_text)
.chunk_by(|(name, _)| name.split_once('@').map(|t| t.0).unwrap_or(name)))
.into_iter()
.map(|(bookmark, mut refs)| {
let local = refs.any(|r| !r.contains('@'));
let help = refs.find_map(|(_, help)| help);
let local = help.is_some();
let mine = prefix.as_ref().is_some_and(|p| bookmark.starts_with(p));
let display_order = match (local, mine) {
@ -119,7 +166,9 @@ pub fn bookmarks() -> Vec<CompletionCandidate> {
(false, true) => 2,
(false, false) => 3,
};
CompletionCandidate::new(bookmark).display_order(Some(display_order))
CompletionCandidate::new(bookmark)
.help(help)
.display_order(Some(display_order))
})
.collect())
})

View file

@ -64,10 +64,10 @@ fn test_bookmark_names() {
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "bookmark", "rename", ""]);
insta::assert_snapshot!(stdout, @r"
aaa-local
aaa-tracked
bbb-local
bbb-tracked
aaa-local x
aaa-tracked x
bbb-local x
bbb-tracked x
--repository Path to repository to operate on
--ignore-working-copy Don't snapshot the working copy, and don't update it
--ignore-immutable Allow rewriting immutable commits
@ -82,20 +82,20 @@ fn test_bookmark_names() {
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "bookmark", "rename", "a"]);
insta::assert_snapshot!(stdout, @r"
aaa-local
aaa-tracked
aaa-local x
aaa-tracked x
");
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "bookmark", "delete", "a"]);
insta::assert_snapshot!(stdout, @r"
aaa-local
aaa-tracked
aaa-local x
aaa-tracked x
");
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "bookmark", "forget", "a"]);
insta::assert_snapshot!(stdout, @r"
aaa-local
aaa-tracked
aaa-local x
aaa-tracked x
aaa-untracked
");
@ -104,39 +104,39 @@ fn test_bookmark_names() {
&["--", "jj", "bookmark", "list", "--bookmark", "a"],
);
insta::assert_snapshot!(stdout, @r"
aaa-local
aaa-tracked
aaa-local x
aaa-tracked x
aaa-untracked
");
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "bookmark", "move", "a"]);
insta::assert_snapshot!(stdout, @r"
aaa-local
aaa-tracked
aaa-local x
aaa-tracked x
");
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "bookmark", "set", "a"]);
insta::assert_snapshot!(stdout, @r"
aaa-local
aaa-tracked
aaa-local x
aaa-tracked x
");
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "bookmark", "track", "a"]);
insta::assert_snapshot!(stdout, @"aaa-untracked@origin");
insta::assert_snapshot!(stdout, @"aaa-untracked@origin x");
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "bookmark", "untrack", "a"]);
insta::assert_snapshot!(stdout, @"aaa-tracked@origin");
insta::assert_snapshot!(stdout, @"aaa-tracked@origin x");
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "git", "push", "-b", "a"]);
insta::assert_snapshot!(stdout, @r"
aaa-local
aaa-tracked
aaa-local x
aaa-tracked x
");
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "git", "fetch", "-b", "a"]);
insta::assert_snapshot!(stdout, @r"
aaa-local
aaa-tracked
aaa-local x
aaa-tracked x
aaa-untracked
");
}
@ -165,7 +165,7 @@ fn test_global_arg_repository_is_respected() {
"a",
],
);
insta::assert_snapshot!(stdout, @"aaa");
insta::assert_snapshot!(stdout, @"aaa (no description set)");
}
#[test]
@ -189,10 +189,10 @@ fn test_aliases_are_resolved() {
let test_env = test_env;
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "b", "rename", "a"]);
insta::assert_snapshot!(stdout, @"aaa");
insta::assert_snapshot!(stdout, @"aaa (no description set)");
let stdout = test_env.jj_cmd_success(&repo_path, &["--", "jj", "b2", "rename", "a"]);
insta::assert_snapshot!(stdout, @"aaa");
insta::assert_snapshot!(stdout, @"aaa (no description set)");
}
#[test]