formatter: flush color escape sequence when raw() stream is requested

This ensures that the data printed through the raw stream is colorized if the
formatter already had color labels, and if the raw data doesn't reset the
surrounding color. This would only matter in templates containing
label(.., raw_escape_sequence() ..) expression.

Fixes #4631
This commit is contained in:
Yuya Nishihara 2024-10-13 20:45:25 +09:00
parent e5e85e781f
commit 8bb806e7b6
7 changed files with 31 additions and 35 deletions

View file

@ -142,7 +142,7 @@ pub(crate) fn cmd_evolog(
commits.truncate(n);
}
if !args.no_graph {
let mut raw_output = formatter.raw();
let mut raw_output = formatter.raw()?;
let mut graph = get_graphlog(graph_style, raw_output.as_mut());
for commit in commits {
let edges = commit

View file

@ -169,7 +169,7 @@ pub(crate) fn cmd_log(
let limit = args.limit.or(args.deprecated_limit).unwrap_or(usize::MAX);
if !args.no_graph {
let mut raw_output = formatter.raw();
let mut raw_output = formatter.raw()?;
let mut graph = get_graphlog(graph_style, raw_output.as_mut());
let forward_iter = TopoGroupedGraphIterator::new(revset.iter_graph());
let iter: Box<dyn Iterator<Item = _>> = if args.reversed {

View file

@ -197,7 +197,7 @@ pub fn show_op_diff(
writeln!(formatter, "Changed commits:")
})?;
if let Some(graph_style) = graph_style {
let mut raw_output = formatter.raw();
let mut raw_output = formatter.raw()?;
let mut graph = get_graphlog(graph_style, raw_output.as_mut());
let graph_iter =

View file

@ -197,7 +197,7 @@ fn do_op_log(
let limit = args.limit.or(args.deprecated_limit).unwrap_or(usize::MAX);
let iter = op_walk::walk_ancestors(slice::from_ref(current_op)).take(limit);
if !args.no_graph {
let mut raw_output = formatter.raw();
let mut raw_output = formatter.raw()?;
let mut graph = get_graphlog(graph_style, raw_output.as_mut());
for op in iter {
let op = op?;

View file

@ -366,15 +366,11 @@ impl<'a> DiffRenderer<'a> {
tool,
)
}
DiffToolMode::Dir => generate_diff(
ui,
formatter.raw().as_mut(),
from_tree,
to_tree,
matcher,
tool,
)
.map_err(DiffRenderError::DiffGenerate),
DiffToolMode::Dir => {
let mut writer = formatter.raw()?;
generate_diff(ui, writer.as_mut(), from_tree, to_tree, matcher, tool)
.map_err(DiffRenderError::DiffGenerate)
}
}?;
}
}
@ -1102,9 +1098,10 @@ pub fn show_file_by_file_diff(
let left_path = create_file(left_path, &left_wc_dir, left_value)?;
let right_path = create_file(right_path, &right_wc_dir, right_value)?;
let mut writer = formatter.raw()?;
invoke_external_diff(
ui,
formatter.raw().as_mut(),
writer.as_mut(),
tool,
&maplit::hashmap! {
"left" => left_path.to_str().expect("temp_dir should be valid utf-8"),

View file

@ -34,7 +34,7 @@ use itertools::Itertools;
pub trait Formatter: Write {
/// Returns the backing `Write`. This is useful for writing data that is
/// already formatted, such as in the graphical log.
fn raw(&mut self) -> Box<dyn Write + '_>;
fn raw(&mut self) -> io::Result<Box<dyn Write + '_>>;
fn push_label(&mut self, label: &str) -> io::Result<()>;
@ -203,8 +203,8 @@ impl<W: Write> Write for PlainTextFormatter<W> {
}
impl<W: Write> Formatter for PlainTextFormatter<W> {
fn raw(&mut self) -> Box<dyn Write + '_> {
Box::new(self.output.by_ref())
fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {
Ok(Box::new(self.output.by_ref()))
}
fn push_label(&mut self, _label: &str) -> io::Result<()> {
@ -238,8 +238,8 @@ impl<W: Write> Write for SanitizingFormatter<W> {
}
impl<W: Write> Formatter for SanitizingFormatter<W> {
fn raw(&mut self) -> Box<dyn Write + '_> {
Box::new(self.output.by_ref())
fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {
Ok(Box::new(self.output.by_ref()))
}
fn push_label(&mut self, _label: &str) -> io::Result<()> {
@ -541,8 +541,9 @@ impl<W: Write> Write for ColorFormatter<W> {
}
impl<W: Write> Formatter for ColorFormatter<W> {
fn raw(&mut self) -> Box<dyn Write + '_> {
Box::new(self.output.by_ref())
fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {
self.write_new_style()?;
Ok(Box::new(self.output.by_ref()))
}
fn push_label(&mut self, label: &str) -> io::Result<()> {
@ -626,8 +627,7 @@ impl FormatRecorder {
FormatOp::PushLabel(label) => formatter.push_label(label)?,
FormatOp::PopLabel => formatter.pop_label()?,
FormatOp::RawEscapeSequence(raw_escape_sequence) => {
// TODO(#4631): process "buffered" labels.
formatter.raw().write_all(raw_escape_sequence)?;
formatter.raw()?.write_all(raw_escape_sequence)?;
}
}
}
@ -660,8 +660,8 @@ impl<'a> Write for RawEscapeSequenceRecorder<'a> {
}
impl Formatter for FormatRecorder {
fn raw(&mut self) -> Box<dyn Write + '_> {
Box::new(RawEscapeSequenceRecorder(self))
fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {
Ok(Box::new(RawEscapeSequenceRecorder(self)))
}
fn push_label(&mut self, label: &str) -> io::Result<()> {
@ -1267,21 +1267,21 @@ mod tests {
fn test_raw_format_recorder() {
// Note: similar to test_format_recorder above
let mut recorder = FormatRecorder::new();
write!(recorder.raw(), " outer1 ").unwrap();
write!(recorder.raw().unwrap(), " outer1 ").unwrap();
recorder.push_label("inner").unwrap();
write!(recorder.raw(), " inner1 ").unwrap();
write!(recorder.raw(), " inner2 ").unwrap();
write!(recorder.raw().unwrap(), " inner1 ").unwrap();
write!(recorder.raw().unwrap(), " inner2 ").unwrap();
recorder.pop_label().unwrap();
write!(recorder.raw(), " outer2 ").unwrap();
write!(recorder.raw().unwrap(), " outer2 ").unwrap();
// No non-raw output to label
// Replayed raw escape sequences are labeled.
let config = config_from_string(r#" colors.inner = "red" "#);
let mut output: Vec<u8> = vec![];
let mut formatter = ColorFormatter::for_config(&mut output, &config, false).unwrap();
recorder.replay(&mut formatter).unwrap();
drop(formatter);
insta::assert_snapshot!(
String::from_utf8(output).unwrap(), @" outer1 inner1 inner2 outer2 ");
String::from_utf8(output).unwrap(), @" outer1  inner1 inner2  outer2 ");
let mut output: Vec<u8> = vec![];
let mut formatter = ColorFormatter::for_config(&mut output, &config, false).unwrap();
@ -1295,6 +1295,6 @@ mod tests {
.unwrap();
drop(formatter);
insta::assert_snapshot!(
String::from_utf8(output).unwrap(), @" outer1 inner1 inner2 outer2 ");
String::from_utf8(output).unwrap(), @" outer1  inner1 inner2  outer2 ");
}
}

View file

@ -190,8 +190,7 @@ pub struct RawEscapeSequenceTemplate<T>(pub T);
impl<T: Template> Template for RawEscapeSequenceTemplate<T> {
fn format(&self, formatter: &mut TemplateFormatter) -> io::Result<()> {
let rewrap = formatter.rewrap_fn();
let mut raw_formatter = PlainTextFormatter::new(formatter.raw());
// TODO(#4631): process "buffered" labels.
let mut raw_formatter = PlainTextFormatter::new(formatter.raw()?);
self.0.format(&mut rewrap(&mut raw_formatter))
}
}
@ -709,7 +708,7 @@ impl<'a> TemplateFormatter<'a> {
move |formatter| TemplateFormatter::new(formatter, error_handler)
}
pub fn raw(&mut self) -> Box<dyn Write + '_> {
pub fn raw(&mut self) -> io::Result<Box<dyn Write + '_>> {
self.formatter.raw()
}