cli: respect color choice in graph logs too

This patch makes it so we use color in the graph iff we use it other
output. We currently always use color except for in the smoke tests,
so it has no effect in practice. It's easy to turn off color when
stdout is redirected (using the `atty` crate), but I haven't done that
because I occasionally pipe `jj log` output to `less` and I want color
then.
This commit is contained in:
Martin von Zweigbergk 2021-06-02 21:43:00 -07:00
parent b50ef1410d
commit 82b62e92f1
2 changed files with 39 additions and 25 deletions

View File

@ -59,7 +59,7 @@ use pest::Parser;
use self::chrono::{FixedOffset, TimeZone, Utc}; use self::chrono::{FixedOffset, TimeZone, Utc};
use crate::commands::CommandError::UserError; use crate::commands::CommandError::UserError;
use crate::diff_edit::DiffEditError; use crate::diff_edit::DiffEditError;
use crate::formatter::{ColorFormatter, Formatter}; use crate::formatter::Formatter;
use crate::graphlog::{AsciiGraphDrawer, Edge}; use crate::graphlog::{AsciiGraphDrawer, Edge};
use crate::template_parser::TemplateParser; use crate::template_parser::TemplateParser;
use crate::templater::Template; use crate::templater::Template;
@ -1055,7 +1055,7 @@ fn cmd_diff(
let summary = from_tree.diff_summary(&to_tree); let summary = from_tree.diff_summary(&to_tree);
show_diff_summary(ui, repo.working_copy_path(), &summary)?; show_diff_summary(ui, repo.working_copy_path(), &summary)?;
} else { } else {
let mut formatter = ui.formatter(); let mut formatter = ui.stdout_formatter();
formatter.add_label(String::from("diff"))?; formatter.add_label(String::from("diff"))?;
for (path, diff) in from_tree.diff(&to_tree) { for (path, diff) in from_tree.diff(&to_tree) {
let ui_path = ui.format_file_path(repo.working_copy_path(), &path); let ui_path = ui.format_file_path(repo.working_copy_path(), &path);
@ -1296,7 +1296,7 @@ fn cmd_log(
let template = let template =
crate::template_parser::parse_commit_template(repo.as_repo_ref(), &template_string); crate::template_parser::parse_commit_template(repo.as_repo_ref(), &template_string);
let mut formatter = ui.formatter(); let mut formatter = ui.stdout_formatter();
let mut formatter = formatter.as_mut(); let mut formatter = formatter.as_mut();
formatter.add_label(String::from("log"))?; formatter.add_label(String::from("log"))?;
@ -1327,12 +1327,11 @@ fn cmd_log(
graphlog_edges.push(Edge::Missing); graphlog_edges.push(Edge::Missing);
} }
let mut buffer = vec![]; let mut buffer = vec![];
// TODO: only use color if requested
{ {
let writer = Box::new(&mut buffer); let writer = Box::new(&mut buffer);
let mut formatter = ColorFormatter::new(writer, ui.settings()); let mut formatter = ui.new_formatter(writer);
let commit = store.get_commit(&index_entry.commit_id()).unwrap(); let commit = store.get_commit(&index_entry.commit_id()).unwrap();
template.format(&commit, &mut formatter)?; template.format(&commit, formatter.as_mut())?;
} }
if !buffer.ends_with(b"\n") { if !buffer.ends_with(b"\n") {
buffer.push(b'\n'); buffer.push(b'\n');
@ -1385,7 +1384,7 @@ fn cmd_obslog(
&template_string, &template_string,
); );
let mut formatter = ui.formatter(); let mut formatter = ui.stdout_formatter();
let mut formatter = formatter.as_mut(); let mut formatter = formatter.as_mut();
formatter.add_label(String::from("log"))?; formatter.add_label(String::from("log"))?;
@ -1402,11 +1401,10 @@ fn cmd_obslog(
edges.push(Edge::direct(predecessor.id().clone())); edges.push(Edge::direct(predecessor.id().clone()));
} }
let mut buffer = vec![]; let mut buffer = vec![];
// TODO: only use color if requested
{ {
let writer = Box::new(&mut buffer); let writer = Box::new(&mut buffer);
let mut formatter = ColorFormatter::new(writer, ui.settings()); let mut formatter = ui.new_formatter(writer);
template.format(&commit, &mut formatter)?; template.format(&commit, formatter.as_mut())?;
} }
if !buffer.ends_with(b"\n") { if !buffer.ends_with(b"\n") {
buffer.push(b'\n'); buffer.push(b'\n');
@ -2227,7 +2225,7 @@ fn cmd_op_log(
let repo = repo_command.repo(); let repo = repo_command.repo();
let head_op = repo.operation().clone(); let head_op = repo.operation().clone();
let head_op_id = head_op.id().clone(); let head_op_id = head_op.id().clone();
let mut formatter = ui.formatter(); let mut formatter = ui.stdout_formatter();
let mut formatter = formatter.as_mut(); let mut formatter = formatter.as_mut();
struct OpTemplate; struct OpTemplate;
impl Template<Operation> for OpTemplate { impl Template<Operation> for OpTemplate {
@ -2276,11 +2274,10 @@ fn cmd_op_log(
edges.push(Edge::direct(parent.id().clone())); edges.push(Edge::direct(parent.id().clone()));
} }
let mut buffer = vec![]; let mut buffer = vec![];
// TODO: only use color if requested
{ {
let writer = Box::new(&mut buffer); let writer = Box::new(&mut buffer);
let mut formatter = ColorFormatter::new(writer, ui.settings()); let mut formatter = ui.new_formatter(writer);
template.format(&op, &mut formatter)?; template.format(&op, formatter.as_mut())?;
} }
if !buffer.ends_with(b"\n") { if !buffer.ends_with(b"\n") {
buffer.push(b'\n'); buffer.push(b'\n');

View File

@ -27,10 +27,23 @@ use crate::templater::TemplateFormatter;
pub struct Ui<'a> { pub struct Ui<'a> {
cwd: PathBuf, cwd: PathBuf,
color: bool,
formatter: Mutex<Box<dyn Formatter + 'a>>, formatter: Mutex<Box<dyn Formatter + 'a>>,
settings: UserSettings, settings: UserSettings,
} }
fn new_formatter<'output>(
settings: &UserSettings,
color: bool,
output: Box<dyn Write + 'output>,
) -> Box<dyn Formatter + 'output> {
if color {
Box::new(ColorFormatter::new(output, &settings))
} else {
Box::new(PlainTextFormatter::new(output))
}
}
impl<'stdout> Ui<'stdout> { impl<'stdout> Ui<'stdout> {
pub fn new( pub fn new(
cwd: PathBuf, cwd: PathBuf,
@ -38,14 +51,11 @@ impl<'stdout> Ui<'stdout> {
is_atty: bool, is_atty: bool,
settings: UserSettings, settings: UserSettings,
) -> Ui<'stdout> { ) -> Ui<'stdout> {
let formatter: Box<dyn Formatter + 'stdout> = if is_atty { let color = is_atty;
Box::new(ColorFormatter::new(stdout, &settings)) let formatter = Mutex::new(new_formatter(&settings, color, stdout));
} else {
Box::new(PlainTextFormatter::new(stdout))
};
let formatter = Mutex::new(formatter);
Ui { Ui {
cwd, cwd,
color,
formatter, formatter,
settings, settings,
} }
@ -65,20 +75,27 @@ impl<'stdout> Ui<'stdout> {
&self.settings &self.settings
} }
pub fn formatter(&self) -> MutexGuard<Box<dyn Formatter + 'stdout>> { pub fn new_formatter<'output>(
&self,
output: Box<dyn Write + 'output>,
) -> Box<dyn Formatter + 'output> {
new_formatter(&self.settings, self.color, output)
}
pub fn stdout_formatter(&self) -> MutexGuard<Box<dyn Formatter + 'stdout>> {
self.formatter.lock().unwrap() self.formatter.lock().unwrap()
} }
pub fn write(&mut self, text: &str) -> io::Result<()> { pub fn write(&mut self, text: &str) -> io::Result<()> {
self.formatter().write_str(text) self.stdout_formatter().write_str(text)
} }
pub fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { pub fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
self.formatter().write_fmt(fmt) self.stdout_formatter().write_fmt(fmt)
} }
pub fn write_error(&mut self, text: &str) -> io::Result<()> { pub fn write_error(&mut self, text: &str) -> io::Result<()> {
let mut formatter = self.formatter(); let mut formatter = self.stdout_formatter();
formatter.add_label(String::from("error"))?; formatter.add_label(String::from("error"))?;
formatter.write_str(text)?; formatter.write_str(text)?;
formatter.remove_label()?; formatter.remove_label()?;
@ -96,7 +113,7 @@ impl<'stdout> Ui<'stdout> {
) )
}); });
let template = crate::template_parser::parse_commit_template(repo, &template_string); let template = crate::template_parser::parse_commit_template(repo, &template_string);
let mut formatter = self.formatter(); let mut formatter = self.stdout_formatter();
let mut template_writer = TemplateFormatter::new(template, formatter.as_mut()); let mut template_writer = TemplateFormatter::new(template, formatter.as_mut());
template_writer.format(commit)?; template_writer.format(commit)?;
Ok(()) Ok(())