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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
use crate::context::Context;
use std::fmt::Display;
use std::ops::Add;
/// A struct representing indentation level of the current code
#[derive(Clone, Copy, Debug)]
pub struct Indent {
/// How many characters a single indent level represents. This is inferred from the configuration
indent_width: usize,
/// The current block indentation level. The base indentation level is 0. Note: this is not the indentation width
block_indent: usize,
/// Any additional indent level that we are in, excluding the block indent. For example, within a multiline table.
additional_indent: usize,
}
impl Indent {
/// Creates a new indentation at the base indent level, inferring indent_width from context.
pub fn new(ctx: &Context) -> Self {
Self {
block_indent: 0,
additional_indent: 0,
indent_width: ctx.config().indent_width,
}
}
/// The current block indentation level
pub fn block_indent(&self) -> usize {
self.block_indent
}
/// The current additional indentation level
pub fn additional_indent(&self) -> usize {
self.additional_indent
}
/// The current width (characters) taken up by indentation
pub fn indent_width(&self) -> usize {
(self.block_indent + self.additional_indent) * self.indent_width
}
/// Recreates an Indent struct with the given additional indent level
pub fn with_additional_indent(&self, additional_indent: usize) -> Self {
Self {
additional_indent,
..*self
}
}
/// Increments the block indentation level by one
pub fn increment_block_indent(&self) -> Self {
Self {
block_indent: self.block_indent.saturating_add(1),
..*self
}
}
// Decrements the block indentation level by one
// pub fn decrement_block_indent(&self) -> Self {
// Self {
// block_indent: self.block_indent.saturating_sub(1),
// ..*self
// }
// }
/// Increments the additional indentation level by one
pub fn increment_additional_indent(&self) -> Self {
Self {
additional_indent: self.additional_indent.saturating_add(1),
..*self
}
}
// Decrements the additional indentation level by one
// pub fn decrement_additional_indent(&self) -> Self {
// Self {
// additional_indent: self.additional_indent.saturating_sub(1),
// ..*self
// }
// }
/// Increases the additional indentation level by amount specified
pub fn add_indent_level(&self, amount: usize) -> Self {
Self {
additional_indent: self.additional_indent.saturating_add(amount),
..*self
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Shape {
/// The current indentation level
indent: Indent,
/// The current width we have taken on the line, excluding any indentation.
offset: usize,
/// The maximum number of characters we want to fit on a line. This is inferred from the configuration
column_width: usize,
}
impl Shape {
/// Creates a new shape at the base indentation level
pub fn new(ctx: &Context) -> Self {
Self {
indent: Indent::new(ctx),
offset: 0,
column_width: ctx.config().column_width,
}
}
/// Sets the column width to the provided width. Normally only used to set an infinite width when testing layouts
pub fn with_column_width(&self, column_width: usize) -> Self {
Self {
column_width,
..*self
}
}
/// Recreates the shape with the provided indentation
pub fn with_indent(&self, indent: Indent) -> Self {
Self { indent, ..*self }
}
/// Recreates the shape with an infinite width. Useful when testing layouts and want to force code onto a single line
pub fn with_infinite_width(&self) -> Self {
self.with_column_width(usize::MAX)
}
/// The current indentation of the shape
pub fn indent(&self) -> Indent {
self.indent
}
/// Increments the block indentation level by one. Alias for `shape.with_indent(shape.indent().increment_block_indent())`
pub fn increment_block_indent(&self) -> Self {
Self {
indent: self.indent.increment_block_indent(),
..*self
}
}
/// Increments the additional indentation level by one. Alias for `shape.with_indent(shape.indent().increment_additional_indent())`
pub fn increment_additional_indent(&self) -> Self {
Self {
indent: self.indent.increment_additional_indent(),
..*self
}
}
/// The width currently taken up for this line
pub fn used_width(&self) -> usize {
self.indent.indent_width() + self.offset
}
/// Check to see whether our current width is above the budget available
pub fn over_budget(&self) -> bool {
self.used_width() > self.column_width
}
/// Adds a width offset to the current width total
pub fn add_width(&self, width: usize) -> Shape {
Self {
offset: self.offset + width,
..*self
}
}
/// Subtracts a width offset from the current width total
pub fn sub_width(&self, width: usize) -> Shape {
Self {
offset: self.offset.saturating_sub(width),
..*self
}
}
/// Resets the offset for the shape
pub fn reset(&self) -> Shape {
Self { offset: 0, ..*self }
}
/// Takes the first line from an item which can be converted into a string, and sets that to the the shape
pub fn take_first_line<T: Display>(&self, item: &T) -> Shape {
let string = format!("{}", item);
let mut lines = string.lines();
let width = lines.next().unwrap_or("").len();
self.add_width(width)
}
/// Takes an item which could possibly span multiple lines. If it spans multiple lines, the shape is reset
/// and the last line is added to the width. If it only takes a single line, we just continue adding to the current
/// width
pub fn take_last_line<T: Display>(&self, item: &T) -> Shape {
let string = format!("{}", item);
let mut lines = string.lines();
let last_item = lines.next_back().unwrap_or("");
// Check if we have any more lines remaining
if lines.count() > 0 {
// Reset the shape and add the last line
self.reset().add_width(last_item.len())
} else {
// Continue adding to the current shape
self.add_width(last_item.len())
}
}
}
impl Add<usize> for Shape {
type Output = Shape;
fn add(self, rhs: usize) -> Shape {
self.add_width(rhs)
}
}