Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Renderer::cut_indicator #172

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 40 additions & 15 deletions src/renderer/display_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ use std::fmt::Display;
use std::ops::Range;
use std::{cmp, fmt};

use unicode_width::UnicodeWidthStr;

use crate::renderer::styled_buffer::StyledBuffer;
use crate::renderer::{stylesheet::Stylesheet, Margin, Style, DEFAULT_TERM_WIDTH};

Expand All @@ -53,6 +55,7 @@ pub(crate) struct DisplayList<'a> {
pub(crate) body: Vec<DisplaySet<'a>>,
pub(crate) stylesheet: &'a Stylesheet,
pub(crate) anonymized_line_numbers: bool,
pub(crate) cut_indicator: &'static str,
}

impl PartialEq for DisplayList<'_> {
Expand Down Expand Up @@ -119,13 +122,21 @@ impl<'a> DisplayList<'a> {
stylesheet: &'a Stylesheet,
anonymized_line_numbers: bool,
term_width: usize,
cut_indicator: &'static str,
) -> DisplayList<'a> {
let body = format_message(message, term_width, anonymized_line_numbers, true);
let body = format_message(
message,
term_width,
anonymized_line_numbers,
cut_indicator,
true,
);

Self {
body,
stylesheet,
anonymized_line_numbers,
cut_indicator,
}
}

Expand All @@ -143,6 +154,7 @@ impl<'a> DisplayList<'a> {
multiline_depth,
self.stylesheet,
self.anonymized_line_numbers,
self.cut_indicator,
buffer,
)?;
}
Expand Down Expand Up @@ -270,6 +282,7 @@ impl DisplaySet<'_> {
}

// Adapted from https://github.com/rust-lang/rust/blob/d371d17496f2ce3a56da76aa083f4ef157572c20/compiler/rustc_errors/src/emitter.rs#L706-L1211
#[allow(clippy::too_many_arguments)]
#[inline]
fn format_line(
&self,
Expand All @@ -278,6 +291,7 @@ impl DisplaySet<'_> {
multiline_depth: usize,
stylesheet: &Stylesheet,
anonymized_line_numbers: bool,
cut_indicator: &'static str,
buffer: &mut StyledBuffer,
) -> fmt::Result {
let line_offset = buffer.num_lines();
Expand Down Expand Up @@ -350,10 +364,15 @@ impl DisplaySet<'_> {
buffer.puts(line_offset, code_offset, &code, Style::new());
if self.margin.was_cut_left() {
// We have stripped some code/whitespace from the beginning, make it clear.
buffer.puts(line_offset, code_offset, "...", *lineno_color);
buffer.puts(line_offset, code_offset, cut_indicator, *lineno_color);
}
if self.margin.was_cut_right(line_len) {
buffer.puts(line_offset, code_offset + taken - 3, "...", *lineno_color);
buffer.puts(
line_offset,
code_offset + taken - cut_indicator.width(),
cut_indicator,
*lineno_color,
);
}

let left: usize = text
Expand Down Expand Up @@ -725,7 +744,7 @@ impl DisplaySet<'_> {
Ok(())
}
DisplayLine::Fold { inline_marks } => {
buffer.puts(line_offset, 0, "...", *stylesheet.line_no());
buffer.puts(line_offset, 0, cut_indicator, *stylesheet.line_no());
if !inline_marks.is_empty() || 0 < multiline_depth {
format_inline_marks(
line_offset,
Expand Down Expand Up @@ -987,12 +1006,13 @@ impl<'a> Iterator for CursorLines<'a> {
}
}

fn format_message(
message: snippet::Message<'_>,
fn format_message<'m>(
message: snippet::Message<'m>,
term_width: usize,
anonymized_line_numbers: bool,
cut_indicator: &'static str,
primary: bool,
) -> Vec<DisplaySet<'_>> {
) -> Vec<DisplaySet<'m>> {
let snippet::Message {
level,
id,
Expand All @@ -1016,6 +1036,7 @@ fn format_message(
!footer.is_empty(),
term_width,
anonymized_line_numbers,
cut_indicator,
));
}

Expand All @@ -1035,6 +1056,7 @@ fn format_message(
annotation,
term_width,
anonymized_line_numbers,
cut_indicator,
false,
));
}
Expand Down Expand Up @@ -1089,13 +1111,14 @@ fn format_label(
result
}

fn format_snippet(
snippet: snippet::Snippet<'_>,
fn format_snippet<'m>(
snippet: snippet::Snippet<'m>,
is_first: bool,
has_footer: bool,
term_width: usize,
anonymized_line_numbers: bool,
) -> DisplaySet<'_> {
cut_indicator: &'static str,
) -> DisplaySet<'m> {
let main_range = snippet.annotations.first().map(|x| x.range.start);
let origin = snippet.origin;
let need_empty_header = origin.is_some() || is_first;
Expand All @@ -1105,6 +1128,7 @@ fn format_snippet(
has_footer,
term_width,
anonymized_line_numbers,
cut_indicator,
);
let header = format_header(origin, main_range, &body.display_lines, is_first);

Expand Down Expand Up @@ -1248,7 +1272,7 @@ fn fold_body(body: Vec<DisplayLine<'_>>) -> Vec<DisplayLine<'_>> {
match unhighlighed_lines.len() {
0 => {}
n if n <= INNER_UNFOLD_SIZE => {
// Rather than render `...`, don't fold
// Rather than render our cut indicator, don't fold
lines.append(&mut unhighlighed_lines);
}
_ => {
Expand Down Expand Up @@ -1287,13 +1311,14 @@ fn fold_body(body: Vec<DisplayLine<'_>>) -> Vec<DisplayLine<'_>> {
lines
}

fn format_body(
snippet: snippet::Snippet<'_>,
fn format_body<'m>(
snippet: snippet::Snippet<'m>,
need_empty_header: bool,
has_footer: bool,
term_width: usize,
anonymized_line_numbers: bool,
) -> DisplaySet<'_> {
cut_indicator: &'static str,
) -> DisplaySet<'m> {
let source_len = snippet.source.len();
if let Some(bigger) = snippet.annotations.iter().find_map(|x| {
// Allow highlighting one past the last character in the source.
Expand Down Expand Up @@ -1626,7 +1651,7 @@ fn format_body(
current_line.to_string().len()
};

let width_offset = 3 + max_line_num_len;
let width_offset = cut_indicator.len() + max_line_num_len;

if span_left_margin == usize::MAX {
span_left_margin = 0;
Expand Down
12 changes: 12 additions & 0 deletions src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//!
//! let renderer = Renderer::styled();
//! println!("{}", renderer.render(snippet));
//! ```

mod display_list;
mod margin;
Expand All @@ -30,6 +31,7 @@ pub struct Renderer {
anonymized_line_numbers: bool,
term_width: usize,
stylesheet: Stylesheet,
cut_indicator: &'static str,
}

impl Renderer {
Expand All @@ -39,6 +41,7 @@ impl Renderer {
anonymized_line_numbers: false,
term_width: DEFAULT_TERM_WIDTH,
stylesheet: Stylesheet::plain(),
cut_indicator: "...",
}
}

Expand Down Expand Up @@ -151,13 +154,22 @@ impl Renderer {
self
}

/// Set the string used for when a long line is cut.
///
/// The default is `...` (three `U+002E` characters).
pub const fn cut_indicator(mut self, string: &'static str) -> Self {
self.cut_indicator = string;
self
}

/// Render a snippet into a `Display`able object
pub fn render<'a>(&'a self, msg: Message<'a>) -> impl Display + 'a {
DisplayList::new(
msg,
&self.stylesheet,
self.anonymized_line_numbers,
self.term_width,
self.cut_indicator,
)
}
}
39 changes: 39 additions & 0 deletions tests/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -955,3 +955,42 @@ error: title
let renderer = Renderer::plain();
assert_data_eq!(renderer.render(input).to_string(), expected);
}

#[test]
fn long_line_cut() {
let source = "abcd abcd abcd abcd abcd abcd abcd";
let input = Level::Error.title("").snippet(
Snippet::source(source)
.line_start(1)
.annotation(Level::Error.span(0..4)),
);
let expected = str![[r#"
error
|
1 | abcd abcd a...
| ^^^^
|
"#]];
let renderer = Renderer::plain().term_width(18);
assert_data_eq!(renderer.render(input).to_string(), expected);
}

#[test]
fn long_line_cut_custom() {
let source = "abcd abcd abcd abcd abcd abcd abcd";
let input = Level::Error.title("").snippet(
Snippet::source(source)
.line_start(1)
.annotation(Level::Error.span(0..4)),
);
// This trims a little less because `…` is visually smaller than `...`.
let expected = str![[r#"
error
|
1 | abcd abcd abc…
| ^^^^
|
"#]];
let renderer = Renderer::plain().term_width(18).cut_indicator("…");
assert_data_eq!(renderer.render(input).to_string(), expected);
}
Loading