cli: highlight "Warning: " and "warning: " headings

This commit is contained in:
Yuya Nishihara 2024-03-24 15:13:42 +09:00
parent 78ce9eae65
commit 1ebe751c42
20 changed files with 92 additions and 57 deletions

View file

@ -1210,7 +1210,7 @@ See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-workin
let settings = &self.settings;
if settings.user_name().is_empty() || settings.user_email().is_empty() {
writeln!(
ui.warning(),
ui.warning_no_heading(),
r#"Name and email not configured. Until configured, your commits will be created with the empty identity, and can't be pushed to remotes. To configure, run:
jj config set --user user.name "Some One"
jj config set --user user.email "someone@example.com""#
@ -1596,7 +1596,7 @@ pub fn print_checkout_stats(
}
if stats.skipped_files != 0 {
writeln!(
ui.warning(),
ui.warning_no_heading(),
"{} of those updates were skipped because there were conflicting changes in the \
working copy.",
stats.skipped_files
@ -1739,7 +1739,10 @@ fn load_template_aliases(
.map_err(|e| e.to_string())
.and_then(|v| aliases_map.insert(&decl, v).map_err(|e| e.to_string()));
if let Err(s) = r {
writeln!(ui.warning(), r#"Failed to load "{TABLE_KEY}.{decl}": {s}"#)?;
writeln!(
ui.warning_no_heading(),
r#"Failed to load "{TABLE_KEY}.{decl}": {s}"#
)?;
}
}
}
@ -2549,7 +2552,7 @@ impl CliRunner {
let new_string_args = expand_args(ui, &self.app, env::args_os(), &config).ok();
if new_string_args.as_ref() != Some(&string_args) {
writeln!(
ui.warning(),
ui.warning_no_heading(),
"Command aliases cannot be loaded from -R/--repository path"
)?;
}

View file

@ -258,8 +258,8 @@ fn cmd_branch_create(
if branch_names.len() > 1 {
writeln!(
ui.warning(),
"warning: Creating multiple branches: {}",
ui.warning_with_heading("warning: "),
"Creating multiple branches: {}",
branch_names.join(", "),
)?;
}
@ -321,8 +321,8 @@ fn cmd_branch_rename(
.any(|(_, remote_ref)| remote_ref.is_tracking())
{
writeln!(
ui.warning(),
"Warning: Branch {old_branch} has tracking remote branches which were not renamed."
ui.warning_with_heading("Warning: "),
"Branch {old_branch} has tracking remote branches which were not renamed."
)?;
writeln!(
ui.hint_with_heading("Hint: "),
@ -370,8 +370,8 @@ fn cmd_branch_set(
if branch_names.len() > 1 {
writeln!(
ui.warning(),
"warning: Updating multiple branches: {}",
ui.warning_with_heading("warning: "),
"Updating multiple branches: {}",
branch_names.join(", "),
)?;
}
@ -487,7 +487,7 @@ fn cmd_branch_delete(
let view = workspace_command.repo().view();
if !args.glob.is_empty() {
writeln!(
ui.warning(),
ui.warning_no_heading(),
"--glob has been deprecated. Please prefix the pattern with `glob:` instead."
)?;
}
@ -514,7 +514,7 @@ fn cmd_branch_forget(
let view = workspace_command.repo().view();
if !args.glob.is_empty() {
writeln!(
ui.warning(),
ui.warning_no_heading(),
"--glob has been deprecated. Please prefix the pattern with `glob:` instead."
)?;
}
@ -541,7 +541,10 @@ fn cmd_branch_track(
let mut names = Vec::new();
for (name, remote_ref) in find_remote_branches(view, &args.names)? {
if remote_ref.is_tracking() {
writeln!(ui.warning(), "Remote branch already tracked: {name}")?;
writeln!(
ui.warning_no_heading(),
"Remote branch already tracked: {name}"
)?;
} else {
names.push(name);
}
@ -574,11 +577,14 @@ fn cmd_branch_untrack(
if name.remote == git::REMOTE_NAME_FOR_LOCAL_GIT_REPO {
// This restriction can be lifted if we want to support untracked @git branches.
writeln!(
ui.warning(),
ui.warning_no_heading(),
"Git-tracking branch cannot be untracked: {name}"
)?;
} else if !remote_ref.is_tracking() {
writeln!(ui.warning(), "Remote branch not tracked yet: {name}")?;
writeln!(
ui.warning_no_heading(),
"Remote branch not tracked yet: {name}"
)?;
} else {
names.push(name);
}

View file

@ -43,12 +43,12 @@ pub(crate) fn cmd_checkout(
args: &CheckoutArgs,
) -> Result<(), CommandError> {
writeln!(
ui.warning(),
"warning: `jj checkout` is deprecated; use `jj new` instead, which is equivalent"
ui.warning_with_heading("warning: "),
"`jj checkout` is deprecated; use `jj new` instead, which is equivalent"
)?;
writeln!(
ui.warning(),
"warning: `jj checkout` will be removed in a future version, and this will be a hard error"
ui.warning_with_heading("warning: "),
"`jj checkout` will be removed in a future version, and this will be a hard error"
)?;
let mut workspace_command = command.workspace_helper(ui)?;
let target = workspace_command.resolve_single_rev(&args.revision)?;

View file

@ -78,7 +78,7 @@ new working-copy commit.
let middle_tree = tx.repo().store().get_root_tree(&tree_id)?;
if !args.paths.is_empty() && middle_tree.id() == base_tree.id() {
writeln!(
ui.warning(),
ui.warning_no_heading(),
"The given paths do not match any file: {}",
args.paths.join(" ")
)?;

View file

@ -257,9 +257,9 @@ pub(crate) fn cmd_config_list(
if !wrote_values {
// Note to stderr explaining why output is empty.
if let Some(name) = &args.name {
writeln!(ui.warning(), "No matching config key for {name}")?;
writeln!(ui.warning_no_heading(), "No matching config key for {name}")?;
} else {
writeln!(ui.warning(), "No config to list")?;
writeln!(ui.warning_no_heading(), "No config to list")?;
}
}
Ok(())

View file

@ -703,7 +703,7 @@ fn cmd_git_clone(
};
if let Err(err) = clean_up_dirs() {
writeln!(
ui.warning(),
ui.warning_no_heading(),
"Failed to clean up {}: {}",
canonical_wc_path.display(),
err
@ -1058,7 +1058,7 @@ struct RejectedBranchUpdateReason {
impl RejectedBranchUpdateReason {
fn print(&self, ui: &Ui) -> io::Result<()> {
writeln!(ui.warning(), "{}", self.message)?;
writeln!(ui.warning_no_heading(), "{}", self.message)?;
if let Some(hint) = &self.hint {
writeln!(ui.hint_with_heading("Hint: "), "{hint}")?;
}
@ -1197,7 +1197,7 @@ fn find_branches_targeted_by_revisions<'a>(
revision_commit_ids.extend(current_branches_revset.iter());
if revision_commit_ids.is_empty() {
writeln!(
ui.warning(),
ui.warning_no_heading(),
"No branches found in the default push revset: \
remote_branches(remote={remote_name})..@"
)?;
@ -1212,7 +1212,7 @@ fn find_branches_targeted_by_revisions<'a>(
if commit_ids.peek().is_none() {
let rev_str: &str = rev_str;
writeln!(
ui.warning(),
ui.warning_no_heading(),
"No branches point to the specified revisions: {rev_str}"
)?;
}

View file

@ -62,8 +62,8 @@ pub(crate) fn cmd_init(
if args.git || args.git_repo.is_some() {
git::git_init(ui, command, &wc_path, colocate, args.git_repo.as_deref())?;
writeln!(
ui.warning(),
"warning: `--git` and `--git-repo` are deprecated.
ui.warning_with_heading("warning: "),
"`--git` and `--git-repo` are deprecated.
Use `jj git init` instead"
)?;
} else {

View file

@ -249,18 +249,18 @@ pub(crate) fn cmd_log(
if only_path == "." && workspace_command.parse_file_path(only_path)?.is_root() {
// For users of e.g. Mercurial, where `.` indicates the current commit.
writeln!(
ui.warning(),
"warning: The argument {only_path:?} is being interpreted as a path, but this is \
often not useful because all non-empty commits touch '.'. If you meant to show \
the working copy commit, pass -r '@' instead."
ui.warning_with_heading("warning: "),
"The argument {only_path:?} is being interpreted as a path, but this is often not \
useful because all non-empty commits touch '.'. If you meant to show the \
working copy commit, pass -r '@' instead."
)?;
} else if revset.is_empty()
&& revset::parse(only_path, &workspace_command.revset_parse_context()).is_ok()
{
writeln!(
ui.warning(),
"warning: The argument {only_path:?} is being interpreted as a path. To specify a \
revset, pass -r {only_path:?} instead."
ui.warning_with_heading("warning: "),
"The argument {only_path:?} is being interpreted as a path. To specify a revset, \
pass -r {only_path:?} instead."
)?;
}
}

View file

@ -26,12 +26,12 @@ pub(crate) fn cmd_merge(
args: &new::NewArgs,
) -> Result<(), CommandError> {
writeln!(
ui.warning(),
"warning: `jj merge` is deprecated; use `jj new` instead, which is equivalent"
ui.warning_with_heading("warning: "),
"`jj merge` is deprecated; use `jj new` instead, which is equivalent"
)?;
writeln!(
ui.warning(),
"warning: `jj merge` will be removed in a future version, and this will be a hard error"
ui.warning_with_heading("warning: "),
"`jj merge` will be removed in a future version, and this will be a hard error"
)?;
if args.revisions.len() < 2 {
return Err(cli_error("Merge requires at least two revisions"));

View file

@ -63,12 +63,12 @@ pub(crate) fn cmd_move(
args: &MoveArgs,
) -> Result<(), CommandError> {
writeln!(
ui.warning(),
"warning: `jj move` is deprecated; use `jj squash` instead, which is equivalent"
ui.warning_with_heading("warning: "),
"`jj move` is deprecated; use `jj squash` instead, which is equivalent"
)?;
writeln!(
ui.warning(),
"warning: `jj move` will be removed in a future version, and this will be a hard error"
ui.warning_with_heading("warning: "),
"`jj move` will be removed in a future version, and this will be a hard error"
)?;
let mut workspace_command = command.workspace_helper(ui)?;
let source = workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"))?;

View file

@ -383,7 +383,7 @@ fn cmd_op_abandon(
let old_op_id = locked_ws.locked_wc().old_operation_id();
if old_op_id != current_head_op.id() {
writeln!(
ui.warning(),
ui.warning_no_heading(),
"The working copy operation {} is not updated because it differs from the repo {}.",
short_operation_hash(old_op_id),
short_operation_hash(current_head_op.id()),

View file

@ -94,7 +94,7 @@ don't make any changes, then the operation will be aborted.
if selected_tree_id == base_tree.id() {
// The user selected nothing, so the first commit will be empty.
writeln!(
ui.warning(),
ui.warning_no_heading(),
"The given paths do not match any file: {}",
args.paths.join(" ")
)?;

View file

@ -199,9 +199,9 @@ from the source will be moved into the destination.
.is_ok()
{
writeln!(
ui.warning(),
"warning: The argument {only_path:?} is being interpreted as a path. To \
specify a revset, pass -r {only_path:?} instead."
ui.warning_with_heading("warning: "),
"The argument {only_path:?} is being interpreted as a path. To specify a \
revset, pass -r {only_path:?} instead."
)?;
}
}

View file

@ -130,9 +130,9 @@ fn cmd_util_completion(
let mut app = command.app().clone();
let warn = |shell| {
writeln!(
ui.warning(),
"Warning: `jj util completion --{shell}` will be removed in a future version, and \
this will be a hard error"
ui.warning_with_heading("Warning: "),
"`jj util completion --{shell}` will be removed in a future version, and this will be \
a hard error"
)?;
writeln!(
ui.hint_with_heading("Hint: "),

View file

@ -2,6 +2,7 @@
"error" = "red"
"warning" = "yellow"
"hint" = "cyan"
"warning heading" = { bold = true }
"hint heading" = { bold = true }
"conflict_description" = "yellow"

View file

@ -392,7 +392,7 @@ pub fn print_failed_git_export(
failed_branches: &[FailedRefExport],
) -> Result<(), std::io::Error> {
if !failed_branches.is_empty() {
writeln!(ui.warning(), "Failed to export some branches:")?;
writeln!(ui.warning_no_heading(), "Failed to export some branches:")?;
let mut formatter = ui.stderr_formatter();
for FailedRefExport { name, reason } in failed_branches {
write!(formatter, " ")?;

View file

@ -324,7 +324,12 @@ pub fn generate_diff(
let exit_status = child.wait().map_err(ExternalToolError::Io)?;
tracing::info!(?cmd, ?exit_status, "The external diff generator exited:");
if !exit_status.success() {
writeln!(ui.warning(), "{}", format_tool_aborted(&exit_status)).ok();
writeln!(
ui.warning_no_heading(),
"{}",
format_tool_aborted(&exit_status)
)
.ok();
}
copy_result.map_err(ExternalToolError::Io)?;
Ok(())

View file

@ -62,7 +62,10 @@ pub fn load_revset_aliases(
.map_err(|e| e.to_string())
.and_then(|v| aliases_map.insert(&decl, v).map_err(|e| e.to_string()));
if let Err(s) = r {
writeln!(ui.warning(), r#"Failed to load "{TABLE_KEY}.{decl}": {s}"#)?;
writeln!(
ui.warning_no_heading(),
r#"Failed to load "{TABLE_KEY}.{decl}": {s}"#
)?;
}
}
}

View file

@ -292,7 +292,7 @@ impl Ui {
Err(e) => {
// The pager executable couldn't be found or couldn't be run
writeln!(
self.warning(),
self.warning_no_heading(),
"Failed to spawn pager '{name}': {e}. Consider using the `:builtin` \
pager.",
name = self.pager_cmd.split_name(),
@ -386,10 +386,19 @@ impl Ui {
HeadingLabeledWriter::new(self.stderr_formatter(), "hint", heading)
}
pub fn warning(&self) -> LabeledWriter<Box<dyn Formatter + '_>, &'static str> {
/// Writer to print warning without the "Warning: " heading.
pub fn warning_no_heading(&self) -> LabeledWriter<Box<dyn Formatter + '_>, &'static str> {
LabeledWriter::new(self.stderr_formatter(), "warning")
}
/// Writer to print warning with the given heading.
pub fn warning_with_heading<H: fmt::Display>(
&self,
heading: H,
) -> HeadingLabeledWriter<Box<dyn Formatter + '_>, &'static str, H> {
HeadingLabeledWriter::new(self.stderr_formatter(), "warning", heading)
}
pub fn error(&self) -> LabeledWriter<Box<dyn Formatter + '_>, &'static str> {
LabeledWriter::new(self.stderr_formatter(), "error")
}
@ -474,7 +483,7 @@ impl Ui {
return Ok(choice);
}
writeln!(self.warning(), "unrecognized response")?;
writeln!(self.warning_no_heading(), "unrecognized response")?;
}
}

View file

@ -388,6 +388,8 @@ fn test_color_config() {
#[test]
fn test_color_ui_messages() {
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");
test_env.add_config("ui.color = 'always'");
// hint and error
@ -397,6 +399,12 @@ fn test_color_ui_messages() {
Run `jj config set --user ui.default-command log` to disable this message.
Error: There is no jj repo in "."
"###);
// warning
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "@"]);
insta::assert_snapshot!(stderr, @r###"
warning: The argument "@" is being interpreted as a path. To specify a revset, pass -r "@" instead.
"###);
}
#[test]