Skip to content

Commit

Permalink
feat(graphical): Add wrap_lines: bool option allowing wrapping be d…
Browse files Browse the repository at this point in the history
…isabled entirely (#328)
  • Loading branch information
zanieb authored Jan 4, 2024
1 parent 7ff4f87 commit b074446
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 6 deletions.
15 changes: 14 additions & 1 deletion src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub struct MietteHandlerOpts {
pub(crate) tab_width: Option<usize>,
pub(crate) with_cause_chain: Option<bool>,
pub(crate) break_words: Option<bool>,
pub(crate) wrap_lines: Option<bool>,
pub(crate) word_separator: Option<textwrap::WordSeparator>,
pub(crate) word_splitter: Option<textwrap::WordSplitter>,
}
Expand Down Expand Up @@ -89,6 +90,16 @@ impl MietteHandlerOpts {
self
}

/// If true, long lines can be wrapped.
///
/// If false, long lines will not be broken when they exceed the width.
///
/// Defaults to true.
pub fn wrap_lines(mut self, wrap_lines: bool) -> Self {
self.wrap_lines = Some(wrap_lines);
self
}

/// If true, long words can be broken when wrapping.
///
/// If false, long words will not be broken when they exceed the width.
Expand All @@ -98,7 +109,6 @@ impl MietteHandlerOpts {
self.break_words = Some(break_words);
self
}

/// Sets the `textwrap::WordSeparator` to use when determining wrap points.
pub fn word_separator(mut self, word_separator: textwrap::WordSeparator) -> Self {
self.word_separator = Some(word_separator);
Expand Down Expand Up @@ -260,6 +270,9 @@ impl MietteHandlerOpts {
if let Some(b) = self.break_words {
handler = handler.with_break_words(b)
}
if let Some(b) = self.wrap_lines {
handler = handler.with_wrap_lines(b)
}
if let Some(s) = self.word_separator {
handler = handler.with_word_separator(s)
}
Expand Down
54 changes: 49 additions & 5 deletions src/handlers/graphical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub struct GraphicalReportHandler {
pub(crate) context_lines: usize,
pub(crate) tab_width: usize,
pub(crate) with_cause_chain: bool,
pub(crate) wrap_lines: bool,
pub(crate) break_words: bool,
pub(crate) word_separator: Option<textwrap::WordSeparator>,
pub(crate) word_splitter: Option<textwrap::WordSplitter>,
Expand All @@ -54,6 +55,7 @@ impl GraphicalReportHandler {
context_lines: 1,
tab_width: 4,
with_cause_chain: true,
wrap_lines: true,
break_words: true,
word_separator: None,
word_splitter: None,
Expand All @@ -69,6 +71,7 @@ impl GraphicalReportHandler {
footer: None,
context_lines: 1,
tab_width: 4,
wrap_lines: true,
with_cause_chain: true,
break_words: true,
word_separator: None,
Expand Down Expand Up @@ -131,6 +134,12 @@ impl GraphicalReportHandler {
self
}

/// Enables or disables wrapping of lines to fit the width.
pub fn with_wrap_lines(mut self, wrap_lines: bool) -> Self {
self.wrap_lines = wrap_lines;
self
}

/// Enables or disables breaking of words during wrapping.
pub fn with_break_words(mut self, break_words: bool) -> Self {
self.break_words = break_words;
Expand Down Expand Up @@ -197,7 +206,7 @@ impl GraphicalReportHandler {
opts = opts.word_splitter(word_splitter);
}

writeln!(f, "{}", textwrap::fill(footer, opts))?;
writeln!(f, "{}", self.wrap(footer, opts))?;
}
Ok(())
}
Expand Down Expand Up @@ -258,7 +267,7 @@ impl GraphicalReportHandler {
opts = opts.word_splitter(word_splitter);
}

writeln!(f, "{}", textwrap::fill(&diagnostic.to_string(), opts))?;
writeln!(f, "{}", self.wrap(&diagnostic.to_string(), opts))?;

if !self.with_cause_chain {
return Ok(());
Expand Down Expand Up @@ -314,10 +323,10 @@ impl GraphicalReportHandler {
inner_renderer.with_cause_chain = false;
inner_renderer.render_report(&mut inner, diag)?;

writeln!(f, "{}", textwrap::fill(&inner, opts))?;
writeln!(f, "{}", self.wrap(&inner, opts))?;
}
ErrorKind::StdError(err) => {
writeln!(f, "{}", textwrap::fill(&err.to_string(), opts))?;
writeln!(f, "{}", self.wrap(&err.to_string(), opts))?;
}
}
}
Expand All @@ -341,7 +350,7 @@ impl GraphicalReportHandler {
opts = opts.word_splitter(word_splitter);
}

writeln!(f, "{}", textwrap::fill(&help.to_string(), opts))?;
writeln!(f, "{}", self.wrap(&help.to_string(), opts))?;
}
Ok(())
}
Expand Down Expand Up @@ -810,6 +819,41 @@ impl GraphicalReportHandler {
Ok(())
}

fn wrap(&self, text: &str, opts: textwrap::Options<'_>) -> String {
if self.wrap_lines {
textwrap::fill(text, opts)
} else {
// Format without wrapping, but retain the indentation options
// Implementation based on `textwrap::indent`
let mut result = String::with_capacity(2 * text.len());
let trimmed_indent = opts.subsequent_indent.trim_end();
for (idx, line) in text.split_terminator('\n').enumerate() {
if idx > 0 {
result.push('\n');
}
if idx == 0 {
if line.trim().is_empty() {
result.push_str(opts.initial_indent.trim_end());
} else {
result.push_str(opts.initial_indent);
}
} else {
if line.trim().is_empty() {
result.push_str(trimmed_indent);
} else {
result.push_str(opts.subsequent_indent);
}
}
result.push_str(line);
}
if text.ends_with('\n') {
// split_terminator will have eaten the final '\n'.
result.push('\n');
}
result
}
}

fn write_linum(&self, f: &mut impl fmt::Write, width: usize, linum: usize) -> fmt::Result {
write!(
f,
Expand Down
44 changes: 44 additions & 0 deletions tests/graphical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,50 @@ fn word_wrap_options() -> Result<(), MietteError> {
Ok(())
}

#[test]
fn wrap_option() -> Result<(), MietteError> {
// A line should break on the width
let out = fmt_report_with_settings(
Report::msg("abc def ghi jkl mno pqr stu vwx yz abc def ghi jkl mno pqr stu vwx yz"),
|handler| handler.with_width(15),
);
let expected = r#" × abc def
│ ghi jkl
│ mno pqr
│ stu vwx
│ yz abc
│ def ghi
│ jkl mno
│ pqr stu
│ vwx yz
"#
.to_string();
assert_eq!(expected, out);

// Unless, wrapping is disabled
let out = fmt_report_with_settings(
Report::msg("abc def ghi jkl mno pqr stu vwx yz abc def ghi jkl mno pqr stu vwx yz"),
|handler| handler.with_width(15).with_wrap_lines(false),
);
let expected =
" × abc def ghi jkl mno pqr stu vwx yz abc def ghi jkl mno pqr stu vwx yz\n".to_string();
assert_eq!(expected, out);

// Then, user-defined new lines should be preserved wrapping is disabled
let out = fmt_report_with_settings(
Report::msg("abc def ghi jkl mno pqr stu vwx yz\nabc def ghi jkl mno pqr stu vwx yz\nabc def ghi jkl mno pqr stu vwx yz"),
|handler| handler.with_width(15).with_wrap_lines(false),
);
let expected = r#" × abc def ghi jkl mno pqr stu vwx yz
│ abc def ghi jkl mno pqr stu vwx yz
│ abc def ghi jkl mno pqr stu vwx yz
"#
.to_string();
assert_eq!(expected, out);

Ok(())
}

#[test]
fn empty_source() -> Result<(), MietteError> {
#[derive(Debug, Diagnostic, Error)]
Expand Down

0 comments on commit b074446

Please sign in to comment.