Skip to content

Commit a65bbc2

Browse files
authored
Merge pull request #213 from Muscraft/long-spans-trim-middle
On long spans, trim the middle of them to make them fit in the terminal width
2 parents 06f12ea + 76f8220 commit a65bbc2

File tree

5 files changed

+208
-40
lines changed

5 files changed

+208
-40
lines changed

src/renderer/margin.rs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub(crate) struct Margin {
1717
/// The end of the line to be displayed.
1818
computed_right: usize,
1919
/// The current width of the terminal. 140 by default and in tests.
20-
term_width: usize,
20+
pub(crate) term_width: usize,
2121
/// The end column of a span label, including the span. Doesn't account for labels not in the
2222
/// same line as the span.
2323
label_right: usize,
@@ -58,18 +58,6 @@ impl Margin {
5858
self.computed_left > 0
5959
}
6060

61-
pub(crate) fn was_cut_right(&self, line_len: usize) -> bool {
62-
let right =
63-
if self.computed_right == self.span_right || self.computed_right == self.label_right {
64-
// Account for the "..." padding given above. Otherwise we end up with code lines that
65-
// do fit but end in "..." as if they were trimmed.
66-
self.computed_right - ELLIPSIS_PASSING
67-
} else {
68-
self.computed_right
69-
};
70-
right < line_len && self.computed_left + self.term_width < line_len
71-
}
72-
7361
fn compute(&mut self, max_line_len: usize) {
7462
// When there's a lot of whitespace (>20), we want to trim it as it is useless.
7563
self.computed_left = if self.whitespace_left > LONG_WHITESPACE {

src/renderer/mod.rs

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -967,21 +967,7 @@ impl Renderer {
967967

968968
let line_offset = buffer.num_lines();
969969

970-
// Left trim
971-
let left = margin.left(str_width(&source_string));
972-
973-
// FIXME: This looks fishy. See #132860.
974-
// Account for unicode characters of width !=0 that were removed.
975-
let mut taken = 0;
976-
source_string.chars().for_each(|ch| {
977-
let next = char_width(ch);
978-
if taken + next <= left {
979-
taken += next;
980-
}
981-
});
982-
983-
let left = taken;
984-
self.draw_line(
970+
let left = self.draw_line(
985971
buffer,
986972
&source_string,
987973
line_info.line_index,
@@ -1136,11 +1122,16 @@ impl Renderer {
11361122
// | x_span
11371123
// <EMPTY LINE>
11381124
//
1125+
let mut overlap = vec![false; annotations.len()];
11391126
let mut annotations_position = vec![];
11401127
let mut line_len: usize = 0;
11411128
let mut p = 0;
11421129
for (i, annotation) in annotations.iter().enumerate() {
11431130
for (j, next) in annotations.iter().enumerate() {
1131+
if overlaps(next, annotation, 0) && j > 1 {
1132+
overlap[i] = true;
1133+
overlap[j] = true;
1134+
}
11441135
if overlaps(next, annotation, 0) // This label overlaps with another one and both
11451136
&& annotation.has_label() // take space (they have text and are not
11461137
&& j > i // multiline lines).
@@ -1488,6 +1479,39 @@ impl Renderer {
14881479
);
14891480
}
14901481
}
1482+
1483+
// We look for individual *long* spans, and we trim the *middle*, so that we render
1484+
// LL | ...= [0, 0, 0, ..., 0, 0];
1485+
// | ^^^^^^^^^^...^^^^^^^ expected `&[u8]`, found `[{integer}; 1680]`
1486+
for (i, (_pos, annotation)) in annotations_position.iter().enumerate() {
1487+
// Skip cases where multiple spans overlap eachother.
1488+
if overlap[i] {
1489+
continue;
1490+
};
1491+
let LineAnnotationType::Singleline = annotation.annotation_type else {
1492+
continue;
1493+
};
1494+
let width = annotation.end.display - annotation.start.display;
1495+
if width > margin.term_width * 2 && width > 10 {
1496+
// If the terminal is *too* small, we keep at least a tiny bit of the span for
1497+
// display.
1498+
let pad = max(margin.term_width / 3, 5);
1499+
// Code line
1500+
buffer.replace(
1501+
line_offset,
1502+
annotation.start.display + pad,
1503+
annotation.end.display - pad,
1504+
self.margin(),
1505+
);
1506+
// Underline line
1507+
buffer.replace(
1508+
line_offset + 1,
1509+
annotation.start.display + pad,
1510+
annotation.end.display - pad,
1511+
self.margin(),
1512+
);
1513+
}
1514+
}
14911515
annotations_position
14921516
.iter()
14931517
.filter_map(|&(_, annotation)| match annotation.annotation_type {
@@ -2036,12 +2060,12 @@ impl Renderer {
20362060
code_offset: usize,
20372061
max_line_num_len: usize,
20382062
margin: Margin,
2039-
) {
2063+
) -> usize {
20402064
// Tabs are assumed to have been replaced by spaces in calling code.
20412065
debug_assert!(!source_string.contains('\t'));
20422066
let line_len = str_width(source_string);
20432067
// Create the source line we will highlight.
2044-
let left = margin.left(line_len);
2068+
let mut left = margin.left(line_len);
20452069
let right = margin.right(line_len);
20462070
// FIXME: The following code looks fishy. See #132860.
20472071
// On long lines, we strip the source line, accounting for unicode.
@@ -2074,10 +2098,15 @@ impl Renderer {
20742098
break;
20752099
}
20762100
}
2101+
2102+
if width_taken > padding {
2103+
left -= width_taken - padding;
2104+
}
2105+
20772106
buffer.puts(
20782107
line_offset,
20792108
code_offset,
2080-
&format!("{placeholder:>width_taken$}"),
2109+
placeholder,
20812110
ElementStyle::LineNumber,
20822111
);
20832112
(width_taken, bytes_taken)
@@ -2092,7 +2121,7 @@ impl Renderer {
20922121
ElementStyle::Quotation,
20932122
);
20942123

2095-
if margin.was_cut_right(line_len) {
2124+
if line_len > right {
20962125
// We have stripped some code/whitespace from the beginning, make it clear.
20972126
let mut char_taken = 0;
20982127
let mut width_taken_inner = 0;
@@ -2121,6 +2150,8 @@ impl Renderer {
21212150
);
21222151

21232152
self.draw_col_separator_no_space(buffer, line_offset, width_offset - 2);
2153+
2154+
left
21242155
}
21252156

21262157
fn draw_range(

src/renderer/styled_buffer.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ impl StyledBuffer {
9999
}
100100
}
101101

102+
pub(crate) fn replace(&mut self, line: usize, start: usize, end: usize, string: &str) {
103+
if start == end {
104+
return;
105+
}
106+
let _ = self.lines[line].drain(start..(end - string.chars().count()));
107+
for (i, c) in string.chars().enumerate() {
108+
self.lines[line][start + i] = StyledChar::new(c, ElementStyle::LineNumber);
109+
}
110+
}
111+
102112
/// For given `line` inserts `string` with `style` before old content of that line,
103113
/// adding lines if needed
104114
pub(crate) fn prepend(&mut self, line: usize, string: &str, style: ElementStyle) {

tests/formatter.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2178,8 +2178,8 @@ fn unicode_cut_handling2() {
21782178
let expected_ascii = str![[r#"
21792179
error: expected item, found `?`
21802180
|
2181-
1 | ...的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?
2182-
| ^ expected item
2181+
1 | ... 的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?
2182+
| ^ expected item
21832183
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
21842184
"#]];
21852185

@@ -2189,8 +2189,8 @@ error: expected item, found `?`
21892189
let expected_unicode = str![[r#"
21902190
error: expected item, found `?`
21912191
2192-
1 │ 宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?
2193-
│ ━ expected item
2192+
1 │ 宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?
2193+
━ expected item
21942194
╰ note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
21952195
"#]];
21962196
let renderer_unicode = renderer_ascii.theme(OutputTheme::Unicode);
@@ -2215,8 +2215,8 @@ fn unicode_cut_handling3() {
22152215
let expected_ascii = str![[r#"
22162216
error: expected item, found `?`
22172217
|
2218-
1 | ...。这是宽的。这是宽的。这是宽的...
2219-
| ^^ expected item
2218+
1 | ... 。这是宽的。这是宽的。这是宽的...
2219+
| ^^ expected item
22202220
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
22212221
"#]];
22222222

@@ -2226,8 +2226,8 @@ error: expected item, found `?`
22262226
let expected_unicode = str![[r#"
22272227
error: expected item, found `?`
22282228
2229-
1 │ 的。这是宽的。这是宽的。这是宽的。…
2230-
│ ━━ expected item
2229+
1 │ 的。这是宽的。这是宽的。这是宽的。…
2230+
━━ expected item
22312231
╰ note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
22322232
"#]];
22332233
let renderer_unicode = renderer_ascii.theme(OutputTheme::Unicode);

0 commit comments

Comments
 (0)