Rollup merge of #79757 - jryans:long-line-tab-handling-early-expand, r=estebank
Replace tabs earlier in diagnostics This replaces tabs earlier in the diagnostics emitting process, which allows various margin calculations to ignore the existence of tabs. It does add a string copy for the source lines that are emitted. Fixes https://github.com/rust-lang/rust/issues/78438 r? `@estebank`
This commit is contained in:
commit
86b900a3ea
4 changed files with 48 additions and 29 deletions
|
@ -644,6 +644,8 @@ impl EmitterWriter {
|
||||||
code_offset: usize,
|
code_offset: usize,
|
||||||
margin: Margin,
|
margin: Margin,
|
||||||
) {
|
) {
|
||||||
|
// Tabs are assumed to have been replaced by spaces in calling code.
|
||||||
|
assert!(!source_string.contains('\t'));
|
||||||
let line_len = source_string.len();
|
let line_len = source_string.len();
|
||||||
// Create the source line we will highlight.
|
// Create the source line we will highlight.
|
||||||
let left = margin.left(line_len);
|
let left = margin.left(line_len);
|
||||||
|
@ -707,7 +709,7 @@ impl EmitterWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
let source_string = match file.get_line(line.line_index - 1) {
|
let source_string = match file.get_line(line.line_index - 1) {
|
||||||
Some(s) => s,
|
Some(s) => replace_tabs(&*s),
|
||||||
None => return Vec::new(),
|
None => return Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1376,8 +1378,17 @@ impl EmitterWriter {
|
||||||
let file = annotated_file.file.clone();
|
let file = annotated_file.file.clone();
|
||||||
let line = &annotated_file.lines[line_idx];
|
let line = &annotated_file.lines[line_idx];
|
||||||
if let Some(source_string) = file.get_line(line.line_index - 1) {
|
if let Some(source_string) = file.get_line(line.line_index - 1) {
|
||||||
let leading_whitespace =
|
let leading_whitespace = source_string
|
||||||
source_string.chars().take_while(|c| c.is_whitespace()).count();
|
.chars()
|
||||||
|
.take_while(|c| c.is_whitespace())
|
||||||
|
.map(|c| {
|
||||||
|
match c {
|
||||||
|
// Tabs are displayed as 4 spaces
|
||||||
|
'\t' => 4,
|
||||||
|
_ => 1,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
if source_string.chars().any(|c| !c.is_whitespace()) {
|
if source_string.chars().any(|c| !c.is_whitespace()) {
|
||||||
whitespace_margin = min(whitespace_margin, leading_whitespace);
|
whitespace_margin = min(whitespace_margin, leading_whitespace);
|
||||||
}
|
}
|
||||||
|
@ -1502,7 +1513,7 @@ impl EmitterWriter {
|
||||||
|
|
||||||
self.draw_line(
|
self.draw_line(
|
||||||
&mut buffer,
|
&mut buffer,
|
||||||
&unannotated_line,
|
&replace_tabs(&unannotated_line),
|
||||||
annotated_file.lines[line_idx + 1].line_index - 1,
|
annotated_file.lines[line_idx + 1].line_index - 1,
|
||||||
last_buffer_line_num,
|
last_buffer_line_num,
|
||||||
width_offset,
|
width_offset,
|
||||||
|
@ -1598,7 +1609,7 @@ impl EmitterWriter {
|
||||||
);
|
);
|
||||||
// print the suggestion
|
// print the suggestion
|
||||||
draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
|
draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
|
||||||
buffer.append(row_num, line, Style::NoStyle);
|
buffer.append(row_num, &replace_tabs(line), Style::NoStyle);
|
||||||
row_num += 1;
|
row_num += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1930,6 +1941,10 @@ impl FileWithAnnotatedLines {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn replace_tabs(str: &str) -> String {
|
||||||
|
str.replace('\t', " ")
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
|
fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
|
||||||
buffer.puts(line, col, "| ", Style::LineNumber);
|
buffer.puts(line, col, "| ", Style::LineNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,34 +13,13 @@ impl StyledBuffer {
|
||||||
StyledBuffer { text: vec![], styles: vec![] }
|
StyledBuffer { text: vec![], styles: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_tabs(&mut self) {
|
pub fn render(&self) -> Vec<Vec<StyledString>> {
|
||||||
for (line_pos, line) in self.text.iter_mut().enumerate() {
|
// Tabs are assumed to have been replaced by spaces in calling code.
|
||||||
let mut tab_pos = vec![];
|
assert!(self.text.iter().all(|r| !r.contains(&'\t')));
|
||||||
for (pos, c) in line.iter().enumerate() {
|
|
||||||
if *c == '\t' {
|
|
||||||
tab_pos.push(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// start with the tabs at the end of the line to replace them with 4 space chars
|
|
||||||
for pos in tab_pos.iter().rev() {
|
|
||||||
assert_eq!(line.remove(*pos), '\t');
|
|
||||||
// fix the position of the style to match up after replacing the tabs
|
|
||||||
let s = self.styles[line_pos].remove(*pos);
|
|
||||||
for _ in 0..4 {
|
|
||||||
line.insert(*pos, ' ');
|
|
||||||
self.styles[line_pos].insert(*pos, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(&mut self) -> Vec<Vec<StyledString>> {
|
|
||||||
let mut output: Vec<Vec<StyledString>> = vec![];
|
let mut output: Vec<Vec<StyledString>> = vec![];
|
||||||
let mut styled_vec: Vec<StyledString> = vec![];
|
let mut styled_vec: Vec<StyledString> = vec![];
|
||||||
|
|
||||||
// before we render, replace tabs with spaces
|
|
||||||
self.replace_tabs();
|
|
||||||
|
|
||||||
for (row, row_style) in self.text.iter().zip(&self.styles) {
|
for (row, row_style) in self.text.iter().zip(&self.styles) {
|
||||||
let mut current_style = Style::NoStyle;
|
let mut current_style = Style::NoStyle;
|
||||||
let mut current_text = String::new();
|
let mut current_text = String::new();
|
||||||
|
|
13
src/test/ui/terminal-width/tabs-trimming.rs
Normal file
13
src/test/ui/terminal-width/tabs-trimming.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// Test for #78438: ensure underline alignment with many tabs on the left, long line on the right
|
||||||
|
|
||||||
|
// ignore-tidy-linelength
|
||||||
|
// ignore-tidy-tab
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let money = 42i32;
|
||||||
|
match money {
|
||||||
|
v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
|
||||||
|
//~^ ERROR variable `v` is not bound in all patterns
|
||||||
|
v => println!("Enough money {}", v),
|
||||||
|
}
|
||||||
|
}
|
12
src/test/ui/terminal-width/tabs-trimming.stderr
Normal file
12
src/test/ui/terminal-width/tabs-trimming.stderr
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0408]: variable `v` is not bound in all patterns
|
||||||
|
--> $DIR/tabs-trimming.rs:9:16
|
||||||
|
|
|
||||||
|
LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT...
|
||||||
|
| - ^ ^ pattern doesn't bind `v`
|
||||||
|
| | |
|
||||||
|
| | pattern doesn't bind `v`
|
||||||
|
| variable not in all patterns
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0408`.
|
Loading…
Add table
Add a link
Reference in a new issue