1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! A formatter for SystemRDL.
//!
//! ```text
//! source text
//! |
//! v syntax lossless CST, comments and all
//! v rules one function per node kind
//! v formatter annotated output and alignment
//! v String final rendering
//! ```
//!
//! # A deliberately small intermediate representation
//!
//! Pretty-printers usually build a document IR (Wadler groups, Oppen's
//! algorithm) because their layout decisions depend on rendered width: whether
//! a list fits on one line cannot be known until everything inside it has been
//! laid out, so the decision has to be deferred and the alternatives measured.
//!
//! None of the *line breaking* rules here are width-dependent. Following the PeakRDL style
//! guide, braces always break, statements are one per line, expressions never
//! break, and a parenthesised list breaks when it holds more than one element.
//! Every one of those is decidable from the tree alone, before a single
//! character is written, so this formatter does not need groups, alternatives,
//! or a fitting algorithm.
//!
//! Column alignment does need hindsight. The formatter therefore retains a
//! narrow IR over its ordinary output: semantic row and cell boundaries grouped
//! into list-local scopes. Once every newline is final, an alignment pass
//! measures adjacent one-line rows and inserts padding before the String is
//! returned. Padding never feeds back into layout.
//!
//! # What the formatter will not do
//!
//! Reformat a file the parser did not fully understand. [`format()`] returns
//! [`FormatError`] when the parse reports errors, because the rules assume a
//! tree shape that error recovery does not guarantee, and rewriting a file
//! whose structure was guessed at is how a formatter corrupts code.
//!
//! Preprocessor directives need no separate rule against them, which is the
//! point of treating even the conditionals as trivia: a `` `ifdef `` whose
//! branches hand a brace back and forth leaves the braces unbalanced, and so is
//! refused by the same check as any other input the parser could not follow.
//! Everything else formats like a comment -- its own line, indented with the
//! code around it, payload untouched. See the module docs in
//! [`crate::syntax::parser`] for why ignoring a conditional cannot corrupt
//! the file.
use crate;
use ;
/// Why no formatted output was produced.
/// Formats SystemRDL source.
///
/// There is nothing to configure, deliberately: a formatter earns its value by
/// ending arguments, not by relocating them into a config file. Indentation is
/// four spaces, which is what the PeakRDL style guide asks for.
///
/// The output is verified before it is returned: see `verify`. A caller that
/// gets `Ok` has a guarantee, not just a hope, that only whitespace moved.
///
/// # Errors
/// [`FormatError::Parse`] if `src` does not parse cleanly. The source is left
/// for the caller to report on rather than being passed through unchanged, so
/// that a broken file is never silently mistaken for a formatted one.
///
/// [`FormatError::Corrupted`] if the formatter has a bug.
/// Checks that formatting moved nothing but whitespace.
///
/// The test suite asserts this over the inputs someone thought to write down.
/// Doing it here instead makes it hold for every input there will ever be,
/// which is what justifies a tool that overwrites source files by default. The
/// cost is one extra lex of the output -- nothing next to the parse that
/// produced it.
///
/// Comments are compared alongside the code, trimmed at the end, because a
/// dropped comment is a real loss even though it changes no behaviour. The
/// trim is what lets the formatter tidy trailing spaces inside one.