Skip to content

Commit

Permalink
Rollup merge of #93155 - dtolnay:blockindent, r=nagisa
Browse files Browse the repository at this point in the history
Switch pretty printer to block-based indentation

This PR backports dtolnay/prettyplease@401d60c from the `prettyplease` crate into `rustc_ast_pretty`.

A before and after:

```diff
- let res =
-     ((::alloc::fmt::format as
-          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
-                                                                                                                                        [ArgumentV1; 0])
-                                                                                                                                      as
-                                                                                                                                      &[ArgumentV1; 0]))
-                                                            as
-                                                            Arguments))
-         as String);
+ let res =
+     ((::alloc::fmt::format as
+             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 [ArgumentV1; 0]) as &[ArgumentV1; 0])) as
+             Arguments)) as String);
```

Previously the pretty printer would compute indentation always relative to whatever column a block begins at, like this:

```rust
fn demo(arg1: usize,
        arg2: usize);
```

This is never the thing to do in the dominant contemporary Rust style. Rustfmt's default and the style used by the vast majority of Rust codebases is block indentation:

```rust
fn demo(
    arg1: usize,
    arg2: usize,
);
```

where every indentation level is a multiple of 4 spaces and each level is indented relative to the indentation of the previous line, not the position that the block starts in.

By itself this PR doesn't get perfect formatting in all cases, but it is the smallest possible step in clearly the right direction. More backports from `prettyplease` to tune the ibox/cbox indent levels around various AST node types are upcoming.
  • Loading branch information
matthiaskrgr authored Jan 31, 2022
2 parents 71efe90 + 125c729 commit 1cb22e4
Show file tree
Hide file tree
Showing 22 changed files with 276 additions and 263 deletions.
69 changes: 49 additions & 20 deletions compiler/rustc_ast_pretty/src/pp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,22 @@ pub enum Breaks {
Inconsistent,
}

#[derive(Clone, Copy)]
enum IndentStyle {
/// Vertically aligned under whatever column this block begins at.
///
/// fn demo(arg1: usize,
/// arg2: usize);
Visual,
/// Indented relative to the indentation level of the previous line.
///
/// fn demo(
/// arg1: usize,
/// arg2: usize,
/// );
Block { offset: isize },
}

#[derive(Clone, Copy)]
pub struct BreakToken {
offset: isize,
Expand All @@ -154,7 +170,7 @@ pub struct BreakToken {

#[derive(Clone, Copy)]
pub struct BeginToken {
offset: isize,
indent: IndentStyle,
breaks: Breaks,
}

Expand All @@ -178,7 +194,7 @@ impl Token {
#[derive(Copy, Clone)]
enum PrintFrame {
Fits,
Broken { offset: isize, breaks: Breaks },
Broken { indent: usize, breaks: Breaks },
}

const SIZE_INFINITY: isize = 0xffff;
Expand All @@ -204,6 +220,8 @@ pub struct Printer {
scan_stack: VecDeque<usize>,
/// Stack of blocks-in-progress being flushed by print
print_stack: Vec<PrintFrame>,
/// Level of indentation of current line
indent: usize,
/// Buffered indentation to avoid writing trailing whitespace
pending_indentation: isize,
/// The token most recently popped from the left boundary of the
Expand All @@ -229,6 +247,7 @@ impl Printer {
right_total: 0,
scan_stack: VecDeque::new(),
print_stack: Vec::new(),
indent: 0,
pending_indentation: 0,
last_printed: None,
}
Expand Down Expand Up @@ -368,38 +387,41 @@ impl Printer {
*self
.print_stack
.last()
.unwrap_or(&PrintFrame::Broken { offset: 0, breaks: Breaks::Inconsistent })
.unwrap_or(&PrintFrame::Broken { indent: 0, breaks: Breaks::Inconsistent })
}

fn print_begin(&mut self, token: BeginToken, size: isize) {
if size > self.space {
let col = self.margin - self.space + token.offset;
self.print_stack.push(PrintFrame::Broken { offset: col, breaks: token.breaks });
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,
};
} else {
self.print_stack.push(PrintFrame::Fits);
}
}

fn print_end(&mut self) {
self.print_stack.pop().unwrap();
if let PrintFrame::Broken { indent, .. } = self.print_stack.pop().unwrap() {
self.indent = indent;
}
}

fn print_break(&mut self, token: BreakToken, size: isize) {
let break_offset =
match self.get_top() {
PrintFrame::Fits => None,
PrintFrame::Broken { offset, breaks: Breaks::Consistent } => Some(offset),
PrintFrame::Broken { offset, breaks: Breaks::Inconsistent } => {
if size > self.space { Some(offset) } else { None }
}
};
if let Some(offset) = break_offset {
self.out.push('\n');
self.pending_indentation = offset + token.offset;
self.space = self.margin - (offset + token.offset);
} else {
let fits = match self.get_top() {
PrintFrame::Fits => true,
PrintFrame::Broken { breaks: Breaks::Consistent, .. } => false,
PrintFrame::Broken { breaks: Breaks::Inconsistent, .. } => size <= self.space,
};
if fits {
self.pending_indentation += token.blank_space;
self.space -= token.blank_space;
} else {
self.out.push('\n');
let indent = self.indent as isize + token.offset;
self.pending_indentation = indent;
self.space = self.margin - indent;
}
}

Expand All @@ -422,7 +444,10 @@ impl Printer {

/// "raw box"
pub fn rbox(&mut self, indent: usize, breaks: Breaks) {
self.scan_begin(BeginToken { offset: indent as isize, breaks })
self.scan_begin(BeginToken {
indent: IndentStyle::Block { offset: indent as isize },
breaks,
})
}

/// Inconsistent breaking box
Expand All @@ -435,6 +460,10 @@ impl Printer {
self.rbox(indent, Breaks::Consistent)
}

pub fn visual_align(&mut self) {
self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent });
}

pub fn break_offset(&mut self, n: usize, off: isize) {
self.scan_break(BreakToken { offset: off, blank_space: n as isize })
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
self.word(cmnt.lines[0].clone());
self.hardbreak()
} else {
self.ibox(0);
self.visual_align();
for line in &cmnt.lines {
if !line.is_empty() {
self.word(line.clone());
Expand Down Expand Up @@ -655,7 +655,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
// Outer-box is consistent.
self.cbox(INDENT_UNIT);
// Head-box is inconsistent.
self.ibox(w.len() + 1);
self.ibox(0);
// Keyword that starts the head.
if !w.is_empty() {
self.word_nbsp(w);
Expand Down
24 changes: 16 additions & 8 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,9 @@ impl<'a> State<'a> {
self.print_ident(label.ident);
self.word_space(":");
}
self.head("while");
self.cbox(0);
self.ibox(0);
self.word_nbsp("while");
self.print_expr_as_cond(test);
self.space();
self.print_block_with_attrs(blk, attrs);
Expand All @@ -330,7 +332,9 @@ impl<'a> State<'a> {
self.print_ident(label.ident);
self.word_space(":");
}
self.head("for");
self.cbox(0);
self.ibox(0);
self.word_nbsp("for");
self.print_pat(pat);
self.space();
self.word_space("in");
Expand All @@ -343,12 +347,14 @@ impl<'a> State<'a> {
self.print_ident(label.ident);
self.word_space(":");
}
self.head("loop");
self.cbox(0);
self.ibox(0);
self.word_nbsp("loop");
self.print_block_with_attrs(blk, attrs);
}
ast::ExprKind::Match(ref expr, ref arms) => {
self.cbox(INDENT_UNIT);
self.ibox(INDENT_UNIT);
self.cbox(0);
self.ibox(0);
self.word_nbsp("match");
self.print_expr_as_cond(expr);
self.space();
Expand Down Expand Up @@ -388,7 +394,7 @@ impl<'a> State<'a> {
self.word_space(":");
}
// containing cbox, will be closed by print-block at }
self.cbox(INDENT_UNIT);
self.cbox(0);
// head-box, will be closed by print-block after {
self.ibox(0);
self.print_block_with_attrs(blk, attrs);
Expand All @@ -397,7 +403,7 @@ impl<'a> State<'a> {
self.word_nbsp("async");
self.print_capture_clause(capture_clause);
// cbox/ibox in analogy to the `ExprKind::Block` arm above
self.cbox(INDENT_UNIT);
self.cbox(0);
self.ibox(0);
self.print_block_with_attrs(blk, attrs);
}
Expand Down Expand Up @@ -500,7 +506,9 @@ impl<'a> State<'a> {
self.word("?")
}
ast::ExprKind::TryBlock(ref blk) => {
self.head("try");
self.cbox(0);
self.ibox(0);
self.word_nbsp("try");
self.print_block_with_attrs(blk, attrs)
}
ast::ExprKind::Err => {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::pp::Breaks::Inconsistent;
use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
use crate::pprust::state::{AnnNode, PrintState, State};

use rustc_ast as ast;
use rustc_ast::GenericBound;
Expand Down Expand Up @@ -377,7 +377,7 @@ impl<'a> State<'a> {
self.space_if_not_bol();
self.maybe_print_comment(v.span.lo());
self.print_outer_attributes(&v.attrs);
self.ibox(INDENT_UNIT);
self.ibox(0);
self.print_variant(v);
self.word(",");
self.end();
Expand Down
74 changes: 37 additions & 37 deletions src/test/pretty/ast-stmt-expr-attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,67 +28,67 @@ fn syntax() {
let _ = #[attr] (x as Y);
let _ =
#[attr] while true {
#![attr]
};
#![attr]
};
let _ =
#[attr] while let Some(false) = true {
#![attr]
};
#![attr]
};
let _ =
#[attr] for x in y {
#![attr]
};
#![attr]
};
let _ =
#[attr] loop {
#![attr]
};
#![attr]
};
let _ =
#[attr] match true {
#![attr]
#[attr]
_ => false,
};
#![attr]
#[attr]
_ => false,
};
let _ = #[attr] || #[attr] foo;
let _ = #[attr] move || #[attr] foo;
let _ =
#[attr] ||
#[attr] {
#![attr]
foo
};
#[attr] {
#![attr]
foo
};
let _ =
#[attr] move ||
#[attr] {
#![attr]
foo
};
#[attr] {
#![attr]
foo
};
let _ =
#[attr] ||
{
#![attr]
foo
};
{
#![attr]
foo
};
let _ =
#[attr] move ||
{
#![attr]
foo
};
{
#![attr]
foo
};
let _ =
#[attr] {
#![attr]
};
#![attr]
};
let _ =
#[attr] {
#![attr]
let _ = ();
};
#![attr]
let _ = ();
};
let _ =
#[attr] {
#![attr]
let _ = ();
foo
};
#![attr]
let _ = ();
foo
};
let _ = #[attr] x = y;
let _ = #[attr] (x = y);
let _ = #[attr] x += y;
Expand Down
8 changes: 4 additions & 4 deletions src/test/pretty/block-comment-wchar.pp
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@
// Taken from https://www.unicode.org/Public/UNIDATA/PropList.txt
let chars =
['\x0A', '\x0B', '\x0C', '\x0D', '\x20', '\u{85}', '\u{A0}',
'\u{1680}', '\u{2000}', '\u{2001}', '\u{2002}', '\u{2003}',
'\u{2004}', '\u{2005}', '\u{2006}', '\u{2007}', '\u{2008}',
'\u{2009}', '\u{200A}', '\u{2028}', '\u{2029}', '\u{202F}',
'\u{205F}', '\u{3000}'];
'\u{1680}', '\u{2000}', '\u{2001}', '\u{2002}', '\u{2003}',
'\u{2004}', '\u{2005}', '\u{2006}', '\u{2007}', '\u{2008}',
'\u{2009}', '\u{200A}', '\u{2028}', '\u{2029}', '\u{202F}',
'\u{205F}', '\u{3000}'];
for c in &chars { let ws = c.is_whitespace(); println!("{} {}", c, ws); }
}
Loading

0 comments on commit 1cb22e4

Please sign in to comment.