Skip to content

Commit

Permalink
completion: add help text for bookmark names
Browse files Browse the repository at this point in the history
  • Loading branch information
senekor committed Nov 16, 2024
1 parent 9d31e74 commit 68c0fd6
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 38 deletions.
75 changes: 62 additions & 13 deletions cli/src/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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())
})
}
Expand All @@ -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())
})
}
Expand All @@ -69,23 +99,32 @@ 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)?;

let prefix = config.get::<String>("git.push-bookmark-prefix").ok();

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())
})
Expand All @@ -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);
Expand All @@ -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) {
Expand All @@ -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())
})
Expand Down
50 changes: 25 additions & 25 deletions cli/tests/test_completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
");

Expand All @@ -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
");
}
Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand Down

0 comments on commit 68c0fd6

Please sign in to comment.