Rollup merge of #93513 - dtolnay:linewidth, r=nagisa

Allow any pretty printed line to have at least 60 chars

Follow-up to #93155. The rustc AST pretty printer has a tendency to get stuck in "vertical smear mode" when formatting highly nested code, where it puts a linebreak at *every possible* linebreak opportunity once the indentation goes beyond the pretty printer's target line width:

```rust
...
                                                              ((&([("test"
                                                                       as
                                                                       &str)]
                                                                     as
                                                                     [&str; 1])
                                                                   as
                                                                   &[&str; 1]),
                                                               (&([]
                                                                     as
                                                                     [ArgumentV1; 0])
                                                                   as
                                                                   &[ArgumentV1; 0]))
...
```

```rust
...
                                                                          [(1
                                                                               as
                                                                               i32),
                                                                           (2
                                                                               as
                                                                               i32),
                                                                           (3
                                                                               as
                                                                               i32)]
                                                                             as
                                                                             [i32; 3]
...
```

This is less common after #93155 because that PR greatly reduced the total amount of indentation, but the "vertical smear mode" failure mode is still just as present when you have deeply nested modules, functions, or trait impls, such as in the case of macro-expanded code from `-Zunpretty=expanded`.

Vertical smear mode is never the best way to format highly indented code though. It does not prevent the target line width from being exceeded, and it produces output that is less readable than just a longer line.

This PR makes the pretty printing algorithm allow a minimum of 60 chars on every line independent of indentation. So as code gets more indented, the right margin eventually recedes to make room for formatting without vertical smear.

```console
├─────────────────────────────────────┤
├─────────────────────────────────────┤
├─────────────────────────────────────┤
  ├───────────────────────────────────┤
    ├─────────────────────────────────┤
      ├───────────────────────────────┤
        ├─────────────────────────────┤
          ├───────────────────────────┤
            ├───────────────────────────┤
              ├───────────────────────────┤
            ├───────────────────────────┤
          ├───────────────────────────┤
        ├─────────────────────────────┤
      ├───────────────────────────────┤
    ├─────────────────────────────────┤
  ├───────────────────────────────────┤
├─────────────────────────────────────┤
```
This commit is contained in:
Eric Huss 2022-01-31 20:13:00 -08:00 committed by GitHub
commit 2e39a3f6ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 13 additions and 14 deletions

View file

@ -136,6 +136,7 @@ mod ring;
use ring::RingBuffer;
use std::borrow::Cow;
use std::cmp;
use std::collections::VecDeque;
use std::iter;
@ -199,10 +200,13 @@ enum PrintFrame {
const SIZE_INFINITY: isize = 0xffff;
/// Target line width.
const MARGIN: isize = 78;
/// Every line is allowed at least this much space, even if highly indented.
const MIN_SPACE: isize = 60;
pub struct Printer {
out: String,
/// Width of lines we're constrained to
margin: isize,
/// Number of spaces left on line
space: isize,
/// Ring-buffer of tokens and calculated sizes
@ -237,11 +241,9 @@ struct BufEntry {
impl Printer {
pub fn new() -> Self {
let linewidth = 78;
Printer {
out: String::new(),
margin: linewidth as isize,
space: linewidth as isize,
space: MARGIN,
buf: RingBuffer::new(),
left_total: 0,
right_total: 0,
@ -395,7 +397,7 @@ impl Printer {
self.print_stack.push(PrintFrame::Broken { indent: self.indent, breaks: token.breaks });
self.indent = match token.indent {
IndentStyle::Block { offset } => (self.indent as isize + offset) as usize,
IndentStyle::Visual => (self.margin - self.space) as usize,
IndentStyle::Visual => (MARGIN - self.space) as usize,
};
} else {
self.print_stack.push(PrintFrame::Fits);
@ -421,7 +423,7 @@ impl Printer {
self.out.push('\n');
let indent = self.indent as isize + token.offset;
self.pending_indentation = indent;
self.space = self.margin - indent;
self.space = cmp::max(MARGIN - indent, MIN_SPACE);
}
}

View file

@ -35,8 +35,7 @@ pub fn bar() ({
for<'r> fn(Arguments<'r>) -> String {format})(((::core::fmt::Arguments::new_v1
as
fn(&[&'static str], &[ArgumentV1]) -> Arguments {Arguments::new_v1})((&([("test"
as &str)] as [&str; 1]) as
&[&str; 1]),
as &str)] as [&str; 1]) as &[&str; 1]),
(&([] as [ArgumentV1; 0]) as &[ArgumentV1; 0])) as
Arguments)) as String);
(res as String)

View file

@ -11,7 +11,6 @@ pub fn main() ({
({ } as
()) else if (let Some(a) =
((Some as
fn(i32) -> Option<i32> {Option::<i32>::Some})((3
as i32)) as Option<i32>) as bool) ({ } as ())
as ())
fn(i32) -> Option<i32> {Option::<i32>::Some})((3 as i32)) as
Option<i32>) as bool) ({ } as ()) as ())
} as ())

View file

@ -27,8 +27,7 @@ fn main() {
crate::TokenStream::from(crate::TokenTree::Literal({
let mut iter =
"\"world\"".parse::<crate::TokenStream>().unwrap().into_iter();
if let (Some(crate::TokenTree::Literal(mut lit)),
None) =
if let (Some(crate::TokenTree::Literal(mut lit)), None) =
(iter.next(), iter.next()) {
lit.set_span(crate::Span::recover_proc_macro_span(2));
lit