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
use super::chunk::{Chunk, ChunkPart};
use std::iter::Peekable;
/// Concatenates chunks as long as they fit in the given width.
pub fn prefix<I>(
tokens: &mut Peekable<I>, width: usize, offset: &mut ChunkPart,
) -> Vec<Chunk>
where
I: Iterator<Item = Chunk>,
{
let mut available = width;
let mut chunks = Vec::new();
// Accumulate chunks until it doesn't fit.
loop {
// Look at the next chunk and see if it would fit.
let result = {
let next_chunk = match tokens.peek() {
None => break,
Some(chunk) => chunk,
};
// When considering if the chunk fits, remember that we may
// already have processed part of it.
// So (chunk - width) fits available
// if chunks fits (available + width)
consider_chunk(available + offset.width, next_chunk)
};
match result {
ChunkFitResult::Fits => {
// It fits! Add it and move to the next one.
let mut chunk = tokens.next().unwrap();
// Remember to strip the prefix, in case we took some earlier.
chunk.remove_front(*offset);
// And reset out offset.
offset.length = 0;
offset.width = 0;
available -= chunk.width;
chunks.push(chunk);
continue;
}
ChunkFitResult::FitsBarely => {
// That's it, it's the last one and we're off.
let mut chunk = tokens.next().unwrap();
chunk.remove_front(*offset);
offset.length = 0;
offset.width = 0;
// We know we need to remove the last character.
// Because it's either:
// * A hard stop: there is a newline
// * A compressed chunk: it ends with a space
chunk.remove_last_char();
chunks.push(chunk);
// No need to update `available`,
// as we're ending the line anyway.
break;
}
ChunkFitResult::DoesNotFit => {
break;
}
}
}
chunks
}
/// Result of a fitness test
///
/// Describes how well a chunk fits in the available space.
enum ChunkFitResult {
/// This chunk can fit as-is
Fits,
/// This chunk fits, but it'll be the last one.
/// Additionally, its last char may need to be removed.
FitsBarely,
/// This chunk doesn't fit. Don't even.
DoesNotFit,
}
/// Look at a chunk, and decide how it could fit.
fn consider_chunk(available: usize, chunk: &Chunk) -> ChunkFitResult {
if chunk.width <= available {
// We fits. No question about it.
if chunk.hard_stop {
// Still, we have to stop here.
// And possibly trim a newline.
ChunkFitResult::FitsBarely
} else {
// Nothing special here.
ChunkFitResult::Fits
}
} else if chunk.width == available + 1 {
// We're just SLIGHTLY too big!
// Can we just pop something?
if chunk.ends_with_space {
// Yay!
ChunkFitResult::FitsBarely
} else {
// Noo(
ChunkFitResult::DoesNotFit
}
} else {
// Can't bargain with me.
ChunkFitResult::DoesNotFit
}
}