Skip to content

Commit b074446

Browse files
authoredJan 4, 2024
feat(graphical): Add wrap_lines: bool option allowing wrapping be disabled entirely (#328)
1 parent 7ff4f87 commit b074446

File tree

3 files changed

+107
-6
lines changed

3 files changed

+107
-6
lines changed
 

‎src/handler.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub struct MietteHandlerOpts {
5656
pub(crate) tab_width: Option<usize>,
5757
pub(crate) with_cause_chain: Option<bool>,
5858
pub(crate) break_words: Option<bool>,
59+
pub(crate) wrap_lines: Option<bool>,
5960
pub(crate) word_separator: Option<textwrap::WordSeparator>,
6061
pub(crate) word_splitter: Option<textwrap::WordSplitter>,
6162
}
@@ -89,6 +90,16 @@ impl MietteHandlerOpts {
8990
self
9091
}
9192

93+
/// If true, long lines can be wrapped.
94+
///
95+
/// If false, long lines will not be broken when they exceed the width.
96+
///
97+
/// Defaults to true.
98+
pub fn wrap_lines(mut self, wrap_lines: bool) -> Self {
99+
self.wrap_lines = Some(wrap_lines);
100+
self
101+
}
102+
92103
/// If true, long words can be broken when wrapping.
93104
///
94105
/// If false, long words will not be broken when they exceed the width.
@@ -98,7 +109,6 @@ impl MietteHandlerOpts {
98109
self.break_words = Some(break_words);
99110
self
100111
}
101-
102112
/// Sets the `textwrap::WordSeparator` to use when determining wrap points.
103113
pub fn word_separator(mut self, word_separator: textwrap::WordSeparator) -> Self {
104114
self.word_separator = Some(word_separator);
@@ -260,6 +270,9 @@ impl MietteHandlerOpts {
260270
if let Some(b) = self.break_words {
261271
handler = handler.with_break_words(b)
262272
}
273+
if let Some(b) = self.wrap_lines {
274+
handler = handler.with_wrap_lines(b)
275+
}
263276
if let Some(s) = self.word_separator {
264277
handler = handler.with_word_separator(s)
265278
}

‎src/handlers/graphical.rs

+49-5
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub struct GraphicalReportHandler {
3030
pub(crate) context_lines: usize,
3131
pub(crate) tab_width: usize,
3232
pub(crate) with_cause_chain: bool,
33+
pub(crate) wrap_lines: bool,
3334
pub(crate) break_words: bool,
3435
pub(crate) word_separator: Option<textwrap::WordSeparator>,
3536
pub(crate) word_splitter: Option<textwrap::WordSplitter>,
@@ -54,6 +55,7 @@ impl GraphicalReportHandler {
5455
context_lines: 1,
5556
tab_width: 4,
5657
with_cause_chain: true,
58+
wrap_lines: true,
5759
break_words: true,
5860
word_separator: None,
5961
word_splitter: None,
@@ -69,6 +71,7 @@ impl GraphicalReportHandler {
6971
footer: None,
7072
context_lines: 1,
7173
tab_width: 4,
74+
wrap_lines: true,
7275
with_cause_chain: true,
7376
break_words: true,
7477
word_separator: None,
@@ -131,6 +134,12 @@ impl GraphicalReportHandler {
131134
self
132135
}
133136

137+
/// Enables or disables wrapping of lines to fit the width.
138+
pub fn with_wrap_lines(mut self, wrap_lines: bool) -> Self {
139+
self.wrap_lines = wrap_lines;
140+
self
141+
}
142+
134143
/// Enables or disables breaking of words during wrapping.
135144
pub fn with_break_words(mut self, break_words: bool) -> Self {
136145
self.break_words = break_words;
@@ -197,7 +206,7 @@ impl GraphicalReportHandler {
197206
opts = opts.word_splitter(word_splitter);
198207
}
199208

200-
writeln!(f, "{}", textwrap::fill(footer, opts))?;
209+
writeln!(f, "{}", self.wrap(footer, opts))?;
201210
}
202211
Ok(())
203212
}
@@ -258,7 +267,7 @@ impl GraphicalReportHandler {
258267
opts = opts.word_splitter(word_splitter);
259268
}
260269

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

263272
if !self.with_cause_chain {
264273
return Ok(());
@@ -314,10 +323,10 @@ impl GraphicalReportHandler {
314323
inner_renderer.with_cause_chain = false;
315324
inner_renderer.render_report(&mut inner, diag)?;
316325

317-
writeln!(f, "{}", textwrap::fill(&inner, opts))?;
326+
writeln!(f, "{}", self.wrap(&inner, opts))?;
318327
}
319328
ErrorKind::StdError(err) => {
320-
writeln!(f, "{}", textwrap::fill(&err.to_string(), opts))?;
329+
writeln!(f, "{}", self.wrap(&err.to_string(), opts))?;
321330
}
322331
}
323332
}
@@ -341,7 +350,7 @@ impl GraphicalReportHandler {
341350
opts = opts.word_splitter(word_splitter);
342351
}
343352

344-
writeln!(f, "{}", textwrap::fill(&help.to_string(), opts))?;
353+
writeln!(f, "{}", self.wrap(&help.to_string(), opts))?;
345354
}
346355
Ok(())
347356
}
@@ -810,6 +819,41 @@ impl GraphicalReportHandler {
810819
Ok(())
811820
}
812821

822+
fn wrap(&self, text: &str, opts: textwrap::Options<'_>) -> String {
823+
if self.wrap_lines {
824+
textwrap::fill(text, opts)
825+
} else {
826+
// Format without wrapping, but retain the indentation options
827+
// Implementation based on `textwrap::indent`
828+
let mut result = String::with_capacity(2 * text.len());
829+
let trimmed_indent = opts.subsequent_indent.trim_end();
830+
for (idx, line) in text.split_terminator('\n').enumerate() {
831+
if idx > 0 {
832+
result.push('\n');
833+
}
834+
if idx == 0 {
835+
if line.trim().is_empty() {
836+
result.push_str(opts.initial_indent.trim_end());
837+
} else {
838+
result.push_str(opts.initial_indent);
839+
}
840+
} else {
841+
if line.trim().is_empty() {
842+
result.push_str(trimmed_indent);
843+
} else {
844+
result.push_str(opts.subsequent_indent);
845+
}
846+
}
847+
result.push_str(line);
848+
}
849+
if text.ends_with('\n') {
850+
// split_terminator will have eaten the final '\n'.
851+
result.push('\n');
852+
}
853+
result
854+
}
855+
}
856+
813857
fn write_linum(&self, f: &mut impl fmt::Write, width: usize, linum: usize) -> fmt::Result {
814858
write!(
815859
f,

‎tests/graphical.rs

+44
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,50 @@ fn word_wrap_options() -> Result<(), MietteError> {
218218
Ok(())
219219
}
220220

221+
#[test]
222+
fn wrap_option() -> Result<(), MietteError> {
223+
// A line should break on the width
224+
let out = fmt_report_with_settings(
225+
Report::msg("abc def ghi jkl mno pqr stu vwx yz abc def ghi jkl mno pqr stu vwx yz"),
226+
|handler| handler.with_width(15),
227+
);
228+
let expected = r#" × abc def
229+
│ ghi jkl
230+
│ mno pqr
231+
│ stu vwx
232+
│ yz abc
233+
│ def ghi
234+
│ jkl mno
235+
│ pqr stu
236+
│ vwx yz
237+
"#
238+
.to_string();
239+
assert_eq!(expected, out);
240+
241+
// Unless, wrapping is disabled
242+
let out = fmt_report_with_settings(
243+
Report::msg("abc def ghi jkl mno pqr stu vwx yz abc def ghi jkl mno pqr stu vwx yz"),
244+
|handler| handler.with_width(15).with_wrap_lines(false),
245+
);
246+
let expected =
247+
" × abc def ghi jkl mno pqr stu vwx yz abc def ghi jkl mno pqr stu vwx yz\n".to_string();
248+
assert_eq!(expected, out);
249+
250+
// Then, user-defined new lines should be preserved wrapping is disabled
251+
let out = fmt_report_with_settings(
252+
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"),
253+
|handler| handler.with_width(15).with_wrap_lines(false),
254+
);
255+
let expected = r#" × abc def ghi jkl mno pqr stu vwx yz
256+
│ abc def ghi jkl mno pqr stu vwx yz
257+
│ abc def ghi jkl mno pqr stu vwx yz
258+
"#
259+
.to_string();
260+
assert_eq!(expected, out);
261+
262+
Ok(())
263+
}
264+
221265
#[test]
222266
fn empty_source() -> Result<(), MietteError> {
223267
#[derive(Debug, Diagnostic, Error)]

0 commit comments

Comments
 (0)
Please sign in to comment.