Skip to content
Merged
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
29 changes: 18 additions & 11 deletions src/renderer/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,12 +664,12 @@ fn render_snippet_annotations(
let mut label_right_margin = 0;
let mut max_line_len = 0;
for line_info in annotated_lines {
max_line_len = max(max_line_len, line_info.line.len());
max_line_len = max(max_line_len, str_width(line_info.line));
for ann in &line_info.annotations {
span_right_margin = max(span_right_margin, ann.start.display);
span_right_margin = max(span_right_margin, ann.end.display);
// FIXME: account for labels not in the same line
let label_right = ann.label.as_ref().map_or(0, |l| l.len() + 1);
let label_right = ann.label.as_ref().map_or(0, |l| str_width(l) + 1);
label_right_margin = max(label_right_margin, ann.end.display + label_right);
}
}
Expand Down Expand Up @@ -1995,23 +1995,34 @@ fn draw_line(
// Create the source line we will highlight.
let mut left = margin.left(line_len);
let right = margin.right(line_len);
// FIXME: The following code looks fishy. See #132860.
// On long lines, we strip the source line, accounting for unicode.

let mut taken = 0;
let mut skipped = 0;
let code: String = source_string
.chars()
.skip_while(|ch| {
skipped += char_width(*ch);
skipped <= left
let w = char_width(*ch);
// If `skipped` is less than `left`, always skip the next `ch`,
// even if `ch` is a multi-width char that would make `skipped`
// exceed `left`. This ensures that we do not exceed term width on
// source lines.
if skipped < left {
skipped += w;
true
} else {
false
}
})
.take_while(|ch| {
// Make sure that the trimming on the right will fall within the terminal width.
taken += char_width(*ch);
taken <= (right - left)
})
.collect();

// If we skipped more than `left`, adjust `left` to account for it.
if skipped > left {
left += skipped - left;
}
let placeholder = renderer.decor_style.margin();
let padding = str_width(placeholder);
let (width_taken, bytes_taken) = if margin.was_cut_left() {
Expand All @@ -2027,10 +2038,6 @@ fn draw_line(
}
}

if width_taken > padding {
left -= width_taken - padding;
}

buffer.puts(
line_offset,
code_offset,
Expand Down
207 changes: 154 additions & 53 deletions tests/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2952,111 +2952,212 @@ error: title
}

#[test]
fn unicode_cut_handling2() {
fn trim_unicode_annotate_ascii_end_with_label() {
let source = "/*这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?";
let input = &[Level::ERROR
.primary_title("expected item, found `?`").element(
Snippet::source(source)
.fold(false)
.annotation(AnnotationKind::Primary.span(499..500).label("expected item"))
).element(
Level::NOTE.message("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>")

)];
let input = &[Group::with_level(Level::ERROR).element(
Snippet::source(source).annotation(
AnnotationKind::Primary
.span(499..500)
.label("expected item"),
),
)];

let expected_ascii = str![[r#"
error: expected item, found `?`
|
1 | ... 的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?
| ^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
| ^ expected item
"#]];

let renderer = Renderer::plain();
assert_data_eq!(renderer.render(input), expected_ascii);

let expected_unicode = str![[r#"
error: expected item, found `?`
╭▸
1 │ … 宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?
│ ━ expected item
╰ note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
╰╴ ━ expected item
"#]];
let renderer = renderer.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer.render(input), expected_unicode);
}

#[test]
fn unicode_cut_handling3() {
fn trim_unicode_annotate_ascii_end_no_label() {
let source = "/*这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?";
let input = &[Level::ERROR
.primary_title("expected item, found `?`").element(
Snippet::source(source)
.fold(false)
.annotation(AnnotationKind::Primary.span(251..254).label("expected item"))
).element(
Level::NOTE.message("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>")
let input = &[Group::with_level(Level::ERROR)
.element(Snippet::source(source).annotation(AnnotationKind::Primary.span(499..500)))];

let expected_ascii = str![[r#"
|
1 | ... 这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?
| ^
"#]];

)];
let renderer = Renderer::plain();
assert_data_eq!(renderer.render(input), expected_ascii);

let expected_unicode = str![[r#"
╭▸
1 │ … 。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?
╰╴ ━
"#]];
let renderer = renderer.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer.render(input), expected_unicode);
}

#[test]
fn trim_unicode_annotate_unicode_end_with_label() {
let source = "/*这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/好";
let input = &[Group::with_level(Level::ERROR).element(
Snippet::source(source).annotation(
AnnotationKind::Primary
.span(499..502)
.label("expected item"),
),
)];

let expected_ascii = str![[r#"
error: expected item, found `?`
|
1 | ... 。这是宽的。这是宽的。这是宽的...
| ^^ expected item
1 | ... 的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/好
| ^^ expected item
"#]];

let renderer = Renderer::plain();
assert_data_eq!(renderer.render(input), expected_ascii);

let expected_unicode = str![[r#"
╭▸
1 │ … 宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/好
╰╴ ━━ expected item
"#]];
let renderer = renderer.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer.render(input), expected_unicode);
}

#[test]
fn trim_unicode_annotate_unicode_end_no_label() {
let source = "/*这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/好";
let input = &[Group::with_level(Level::ERROR)
.element(Snippet::source(source).annotation(AnnotationKind::Primary.span(499..502)))];

let expected_ascii = str![[r#"
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
1 | ... 这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/好
| ^^
"#]];

let renderer = Renderer::plain();
assert_data_eq!(renderer.render(input), expected_ascii);

let expected_unicode = str![[r#"
╭▸
1 │ … 。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/好
╰╴ ━━
"#]];
let renderer = renderer.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer.render(input), expected_unicode);
}

#[test]
fn trim_unicode_annotate_unicode_middle_with_label() {
let source = "/*这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?";
let input = &[Group::with_level(Level::ERROR).element(
Snippet::source(source).annotation(
AnnotationKind::Primary
.span(251..254)
.label("expected item"),
),
)];

let expected_ascii = str![[r#"
|
1 | ... 这是宽的。这是宽的。这是宽的。...
| ^^ expected item
"#]];

let renderer = Renderer::plain().term_width(43);
assert_data_eq!(renderer.render(input), expected_ascii);

let expected_unicode = str![[r#"
error: expected item, found `?`
╭▸
1 │ … 的。这是宽的。这是宽的。这是宽的。…
│ ━━ expected item
╰ note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
1 │ … 。这是宽的。这是宽的。这是宽的。这…
╰╴ ━━ expected item
"#]];
let renderer = renderer.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer.render(input), expected_unicode);
}

#[test]
fn unicode_cut_handling4() {
let source = "/*aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/?";
let input = &[Level::ERROR
.primary_title("expected item, found `?`").element(
Snippet::source(source)
.fold(false)
.annotation(AnnotationKind::Primary.span(334..335).label("expected item"))
).element(
Level::NOTE.message("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>")
fn trim_unicode_annotate_unicode_middle_no_label() {
let source = "/*这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?";
let input = &[Group::with_level(Level::ERROR)
.element(Snippet::source(source).annotation(AnnotationKind::Primary.span(251..254)))];

let expected_ascii = str![[r#"
|
1 | ... 是宽的。这是宽的。这是宽的。这...
| ^^
"#]];

let renderer = Renderer::plain().term_width(43);
assert_data_eq!(renderer.render(input), expected_ascii);

let expected_unicode = str![[r#"
╭▸
1 │ … 这是宽的。这是宽的。这是宽的。这是…
╰╴ ━━
"#]];
let renderer = renderer.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer.render(input), expected_unicode);
}

)];
#[test]
fn trim_ascii_annotate_ascii_end_with_label() {
let source = "/*aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/?";
let input = &[Group::with_level(Level::ERROR).element(
Snippet::source(source).annotation(
AnnotationKind::Primary
.span(334..335)
.label("expected item"),
),
)];

let expected_ascii = str![[r#"
error: expected item, found `?`
|
1 | ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/?
| ^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
"#]];

let renderer = Renderer::plain();
assert_data_eq!(renderer.render(input), expected_ascii);

let expected_unicode = str![[r#"
error: expected item, found `?`
╭▸
1 │ …aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/?
│ ━ expected item
╰ note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
╰╴ ━ expected item
"#]];
let renderer = renderer.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer.render(input), expected_unicode);
}

#[test]
fn trim_ascii_annotate_ascii_end_no_label() {
let source = "/*aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/?";
let input = &[Group::with_level(Level::ERROR)
.element(Snippet::source(source).annotation(AnnotationKind::Primary.span(334..335)))];

let expected_ascii = str![[r#"
|
1 | ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/?
| ^
"#]];

let renderer = Renderer::plain();
assert_data_eq!(renderer.render(input), expected_ascii);

let expected_unicode = str![[r#"
╭▸
1 │ …aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/?
╰╴ ━
"#]];
let renderer = renderer.decor_style(DecorStyle::Unicode);
assert_data_eq!(renderer.render(input), expected_unicode);
Expand Down