Skip to content

Commit f489851

Browse files
authored
Merge pull request #707 from jturner314/alt-form-format
Use alternate form flag (#) to disable overflow with ellipsis
2 parents e9ef450 + fe54105 commit f489851

File tree

1 file changed

+166
-114
lines changed

1 file changed

+166
-114
lines changed

src/arrayformat.rs

+166-114
Original file line numberDiff line numberDiff line change
@@ -9,55 +9,68 @@ use super::{ArrayBase, Axis, Data, Dimension, Ix, NdProducer};
99
use crate::aliases::Ix1;
1010
use std::fmt;
1111

12-
const PRINT_ELEMENTS_LIMIT: Ix = 3;
12+
/// Default maximum axis length before overflowing with an ellipsis.
13+
const AXIS_LEN_LIMIT: Ix = 6;
1314

14-
fn format_1d_array<A, S, F>(
15-
view: &ArrayBase<S, Ix1>,
15+
/// The string used as an ellipsis.
16+
const ELLIPSIS: &str = "...";
17+
18+
/// Returns the axis length limit based on whether or not the alternate (`#`)
19+
/// flag was specified on the formatter.
20+
fn axis_len_limit(f: &mut fmt::Formatter<'_>) -> usize {
21+
if f.alternate() {
22+
std::usize::MAX
23+
} else {
24+
AXIS_LEN_LIMIT
25+
}
26+
}
27+
28+
/// Formats the contents of a list of items, using an ellipsis to indicate when
29+
/// the `length` of the list is greater than `limit`.
30+
///
31+
/// # Parameters
32+
///
33+
/// * `f`: The formatter.
34+
/// * `length`: The length of the list.
35+
/// * `limit`: The maximum number of items before overflow.
36+
/// * `separator`: Separator to write between items.
37+
/// * `ellipsis`: Ellipsis for indicating overflow.
38+
/// * `fmt_elem`: A function that formats an element in the list, given the
39+
/// formatter and the index of the item in the list.
40+
fn format_with_overflow<F>(
1641
f: &mut fmt::Formatter<'_>,
17-
mut format: F,
18-
limit: Ix,
42+
length: usize,
43+
limit: usize,
44+
separator: &str,
45+
ellipsis: &str,
46+
mut fmt_elem: F,
1947
) -> fmt::Result
2048
where
21-
F: FnMut(&A, &mut fmt::Formatter<'_>) -> fmt::Result,
22-
S: Data<Elem = A>,
49+
F: FnMut(&mut fmt::Formatter<'_>, usize) -> fmt::Result,
2350
{
24-
let to_be_printed = to_be_printed(view.len(), limit);
25-
26-
let n_to_be_printed = to_be_printed.len();
27-
28-
write!(f, "[")?;
29-
for (j, index) in to_be_printed.into_iter().enumerate() {
30-
match index {
31-
PrintableCell::ElementIndex(i) => {
32-
format(&view[i], f)?;
33-
if j != n_to_be_printed - 1 {
34-
write!(f, ", ")?;
35-
}
36-
}
37-
PrintableCell::Ellipses => write!(f, "..., ")?,
38-
}
39-
}
40-
write!(f, "]")?;
41-
Ok(())
42-
}
43-
44-
enum PrintableCell {
45-
ElementIndex(usize),
46-
Ellipses,
47-
}
48-
49-
// Returns what indexes should be printed for a certain axis.
50-
// If the axis is longer than 2 * limit, a `Ellipses` is inserted
51-
// where indexes are being omitted.
52-
fn to_be_printed(length: usize, limit: usize) -> Vec<PrintableCell> {
53-
if length <= 2 * limit {
54-
(0..length).map(PrintableCell::ElementIndex).collect()
51+
if length == 0 {
52+
// no-op
53+
} else if length <= limit {
54+
fmt_elem(f, 0)?;
55+
(1..length).try_for_each(|i| {
56+
f.write_str(separator)?;
57+
fmt_elem(f, i)
58+
})?;
5559
} else {
56-
let mut v: Vec<PrintableCell> = (0..limit).map(PrintableCell::ElementIndex).collect();
57-
v.push(PrintableCell::Ellipses);
58-
v.extend((length - limit..length).map(PrintableCell::ElementIndex));
59-
v
60+
let edge = limit / 2;
61+
fmt_elem(f, 0)?;
62+
(1..edge).try_for_each(|i| {
63+
f.write_str(separator)?;
64+
fmt_elem(f, i)
65+
})?;
66+
f.write_str(separator)?;
67+
f.write_str(ellipsis)?;
68+
(length - edge..length).try_for_each(|i| {
69+
f.write_str(separator)?;
70+
fmt_elem(f, i)
71+
})?;
6072
}
73+
Ok(())
6174
}
6275

6376
fn format_array<A, S, D, F>(
@@ -80,54 +93,37 @@ where
8093
}
8194
match view.shape() {
8295
// If it's 0 dimensional, we just print out the scalar
83-
[] => format(view.iter().next().unwrap(), f)?,
84-
// We delegate 1-dimensional arrays to a specialized function
85-
[_] => format_1d_array(
86-
&view.view().into_dimensionality::<Ix1>().unwrap(),
87-
f,
88-
format,
89-
limit,
90-
)?,
96+
&[] => format(view.iter().next().unwrap(), f)?,
97+
// We handle 1-D arrays as a special case
98+
&[len] => {
99+
let view = view.view().into_dimensionality::<Ix1>().unwrap();
100+
f.write_str("[")?;
101+
format_with_overflow(f, len, limit, ", ", ELLIPSIS, |f, index| {
102+
format(&view[index], f)
103+
})?;
104+
f.write_str("]")?;
105+
}
91106
// For n-dimensional arrays, we proceed recursively
92107
shape => {
93108
// Cast into a dynamically dimensioned view
94109
// This is required to be able to use `index_axis`
95110
let view = view.view().into_dyn();
96-
// We start by checking what indexes from the first axis should be printed
97-
// We put a `None` in the middle if we are omitting elements
98-
let to_be_printed = to_be_printed(shape[0], limit);
99-
100-
let n_to_be_printed = to_be_printed.len();
101111

102112
let blank_lines = "\n".repeat(shape.len() - 2);
103113
let indent = " ".repeat(depth + 1);
104-
105-
write!(f, "[")?;
106-
for (j, index) in to_be_printed.into_iter().enumerate() {
107-
match index {
108-
PrintableCell::ElementIndex(i) => {
109-
// Indent all but the first line.
110-
if j != 0 {
111-
write!(f, "{}", indent)?;
112-
}
113-
// Proceed recursively with the (n-1)-dimensional slice
114-
format_array(
115-
&view.index_axis(Axis(0), i),
116-
f,
117-
format.clone(),
118-
limit,
119-
depth + 1,
120-
)?;
121-
// We need to add a separator after each slice,
122-
// apart from the last one
123-
if j != n_to_be_printed - 1 {
124-
write!(f, ",\n{}", blank_lines)?
125-
}
126-
}
127-
PrintableCell::Ellipses => write!(f, "{}...,\n{}", indent, blank_lines)?,
128-
}
129-
}
130-
write!(f, "]")?;
114+
let separator = format!(",\n{}{}", blank_lines, indent);
115+
116+
f.write_str("[")?;
117+
format_with_overflow(f, shape[0], limit, &separator, ELLIPSIS, |f, index| {
118+
format_array(
119+
&view.index_axis(Axis(0), index),
120+
f,
121+
format.clone(),
122+
limit,
123+
depth + 1,
124+
)
125+
})?;
126+
f.write_str("]")?;
131127
}
132128
}
133129
Ok(())
@@ -143,7 +139,8 @@ where
143139
S: Data<Elem = A>,
144140
{
145141
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146-
format_array(self, f, <_>::fmt, PRINT_ELEMENTS_LIMIT, 0)
142+
let limit = axis_len_limit(f);
143+
format_array(self, f, <_>::fmt, limit, 0)
147144
}
148145
}
149146

@@ -156,8 +153,10 @@ where
156153
S: Data<Elem = A>,
157154
{
158155
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156+
let limit = axis_len_limit(f);
157+
format_array(self, f, <_>::fmt, limit, 0)?;
158+
159159
// Add extra information for Debug
160-
format_array(self, f, <_>::fmt, PRINT_ELEMENTS_LIMIT, 0)?;
161160
write!(
162161
f,
163162
", shape={:?}, strides={:?}, layout={:?}",
@@ -182,7 +181,8 @@ where
182181
S: Data<Elem = A>,
183182
{
184183
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185-
format_array(self, f, <_>::fmt, PRINT_ELEMENTS_LIMIT, 0)
184+
let limit = axis_len_limit(f);
185+
format_array(self, f, <_>::fmt, limit, 0)
186186
}
187187
}
188188

@@ -195,7 +195,8 @@ where
195195
S: Data<Elem = A>,
196196
{
197197
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198-
format_array(self, f, <_>::fmt, PRINT_ELEMENTS_LIMIT, 0)
198+
let limit = axis_len_limit(f);
199+
format_array(self, f, <_>::fmt, limit, 0)
199200
}
200201
}
201202
/// Format the array using `LowerHex` and apply the formatting parameters used
@@ -207,7 +208,8 @@ where
207208
S: Data<Elem = A>,
208209
{
209210
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210-
format_array(self, f, <_>::fmt, PRINT_ELEMENTS_LIMIT, 0)
211+
let limit = axis_len_limit(f);
212+
format_array(self, f, <_>::fmt, limit, 0)
211213
}
212214
}
213215

@@ -220,7 +222,8 @@ where
220222
S: Data<Elem = A>,
221223
{
222224
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223-
format_array(self, f, <_>::fmt, PRINT_ELEMENTS_LIMIT, 0)
225+
let limit = axis_len_limit(f);
226+
format_array(self, f, <_>::fmt, limit, 0)
224227
}
225228
}
226229

@@ -264,57 +267,89 @@ mod formatting_with_omit {
264267

265268
#[test]
266269
fn dim_1() {
267-
let overflow: usize = 5;
268-
let a = Array1::from_elem((PRINT_ELEMENTS_LIMIT * 2 + overflow,), 1);
270+
let overflow: usize = 2;
271+
let a = Array1::from_elem(AXIS_LEN_LIMIT + overflow, 1);
269272
let actual = format!("{}", a);
270273
let expected = "[1, 1, 1, ..., 1, 1, 1]";
271274
assert_str_eq(expected, &actual);
272275
}
273276

277+
#[test]
278+
fn dim_1_alternate() {
279+
let overflow: usize = 2;
280+
let a = Array1::from_elem(AXIS_LEN_LIMIT + overflow, 1);
281+
let actual = format!("{:#}", a);
282+
let expected = "[1, 1, 1, 1, 1, 1, 1, 1]";
283+
assert_str_eq(expected, &actual);
284+
}
285+
274286
#[test]
275287
fn dim_2_last_axis_overflow() {
276-
let overflow: usize = 3;
277-
let a = Array2::from_elem(
278-
(PRINT_ELEMENTS_LIMIT, PRINT_ELEMENTS_LIMIT * 2 + overflow),
279-
1,
280-
);
288+
let overflow: usize = 2;
289+
let a = Array2::from_elem((AXIS_LEN_LIMIT, AXIS_LEN_LIMIT + overflow), 1);
281290
let actual = format!("{}", a);
282291
let expected = "\
283292
[[1, 1, 1, ..., 1, 1, 1],
293+
[1, 1, 1, ..., 1, 1, 1],
294+
[1, 1, 1, ..., 1, 1, 1],
295+
[1, 1, 1, ..., 1, 1, 1],
284296
[1, 1, 1, ..., 1, 1, 1],
285297
[1, 1, 1, ..., 1, 1, 1]]";
286298
assert_str_eq(expected, &actual);
287299
}
288300

301+
#[test]
302+
fn dim_2_last_axis_overflow_alternate() {
303+
let overflow: usize = 2;
304+
let a = Array2::from_elem((AXIS_LEN_LIMIT, AXIS_LEN_LIMIT + overflow), 1);
305+
let actual = format!("{:#}", a);
306+
let expected = "\
307+
[[1, 1, 1, 1, 1, 1, 1, 1],
308+
[1, 1, 1, 1, 1, 1, 1, 1],
309+
[1, 1, 1, 1, 1, 1, 1, 1],
310+
[1, 1, 1, 1, 1, 1, 1, 1],
311+
[1, 1, 1, 1, 1, 1, 1, 1],
312+
[1, 1, 1, 1, 1, 1, 1, 1]]";
313+
assert_str_eq(expected, &actual);
314+
}
315+
289316
#[test]
290317
fn dim_2_non_last_axis_overflow() {
291-
let overflow: usize = 5;
292-
let a = Array2::from_elem(
293-
(PRINT_ELEMENTS_LIMIT * 2 + overflow, PRINT_ELEMENTS_LIMIT),
294-
1,
295-
);
318+
let overflow: usize = 2;
319+
let a = Array2::from_elem((AXIS_LEN_LIMIT + overflow, AXIS_LEN_LIMIT), 1);
296320
let actual = format!("{}", a);
297321
let expected = "\
298-
[[1, 1, 1],
299-
[1, 1, 1],
300-
[1, 1, 1],
322+
[[1, 1, 1, 1, 1, 1],
323+
[1, 1, 1, 1, 1, 1],
324+
[1, 1, 1, 1, 1, 1],
301325
...,
302-
[1, 1, 1],
303-
[1, 1, 1],
304-
[1, 1, 1]]";
326+
[1, 1, 1, 1, 1, 1],
327+
[1, 1, 1, 1, 1, 1],
328+
[1, 1, 1, 1, 1, 1]]";
329+
assert_str_eq(expected, &actual);
330+
}
331+
332+
#[test]
333+
fn dim_2_non_last_axis_overflow_alternate() {
334+
let overflow: usize = 2;
335+
let a = Array2::from_elem((AXIS_LEN_LIMIT + overflow, AXIS_LEN_LIMIT), 1);
336+
let actual = format!("{:#}", a);
337+
let expected = "\
338+
[[1, 1, 1, 1, 1, 1],
339+
[1, 1, 1, 1, 1, 1],
340+
[1, 1, 1, 1, 1, 1],
341+
[1, 1, 1, 1, 1, 1],
342+
[1, 1, 1, 1, 1, 1],
343+
[1, 1, 1, 1, 1, 1],
344+
[1, 1, 1, 1, 1, 1],
345+
[1, 1, 1, 1, 1, 1]]";
305346
assert_str_eq(expected, &actual);
306347
}
307348

308349
#[test]
309350
fn dim_2_multi_directional_overflow() {
310-
let overflow: usize = 5;
311-
let a = Array2::from_elem(
312-
(
313-
PRINT_ELEMENTS_LIMIT * 2 + overflow,
314-
PRINT_ELEMENTS_LIMIT * 2 + overflow,
315-
),
316-
1,
317-
);
351+
let overflow: usize = 2;
352+
let a = Array2::from_elem((AXIS_LEN_LIMIT + overflow, AXIS_LEN_LIMIT + overflow), 1);
318353
let actual = format!("{}", a);
319354
let expected = "\
320355
[[1, 1, 1, ..., 1, 1, 1],
@@ -327,6 +362,23 @@ mod formatting_with_omit {
327362
assert_str_eq(expected, &actual);
328363
}
329364

365+
#[test]
366+
fn dim_2_multi_directional_overflow_alternate() {
367+
let overflow: usize = 2;
368+
let a = Array2::from_elem((AXIS_LEN_LIMIT + overflow, AXIS_LEN_LIMIT + overflow), 1);
369+
let actual = format!("{:#}", a);
370+
let expected = "\
371+
[[1, 1, 1, 1, 1, 1, 1, 1],
372+
[1, 1, 1, 1, 1, 1, 1, 1],
373+
[1, 1, 1, 1, 1, 1, 1, 1],
374+
[1, 1, 1, 1, 1, 1, 1, 1],
375+
[1, 1, 1, 1, 1, 1, 1, 1],
376+
[1, 1, 1, 1, 1, 1, 1, 1],
377+
[1, 1, 1, 1, 1, 1, 1, 1],
378+
[1, 1, 1, 1, 1, 1, 1, 1]]";
379+
assert_str_eq(expected, &actual);
380+
}
381+
330382
#[test]
331383
fn dim_3_overflow_all() {
332384
let a = Array3::from_shape_fn((20, 10, 7), |(i, j, k)| {

0 commit comments

Comments
 (0)