pub const WIDTH: usize = 80;
pub const WIDEST: usize = 120;
const HANG: usize = 2;
pub fn fold(text: &str, width: usize) -> String {
fold_at(text, width, 0)
}
pub fn fold_at(text: &str, width: usize, tail: usize) -> String {
let lines: Vec<&str> = text.split('\n').collect();
let last = lines.len().saturating_sub(1);
lines
.iter()
.enumerate()
.map(|(at, line)| one_line(line, width, if at == last { tail } else { 0 }))
.collect::<Vec<String>>()
.join("\n")
}
fn one_line(text: &str, width: usize, tail: usize) -> String {
let words: Vec<&str> = text.split_whitespace().collect();
if words.is_empty() {
return String::new();
}
let mut widths: Vec<usize> = words.iter().map(|word| word.chars().count()).collect();
if tail > 0 {
let end = widths.len() - 1;
widths[end] += 1 + tail;
}
let mut out = String::new();
let mut used = 0;
for (word, measure) in words.iter().zip(widths) {
if used == 0 {
out.push_str(word);
used = measure;
} else if used + 1 + measure <= width {
out.push(' ');
out.push_str(word);
used += 1 + measure;
} else {
out.push('\n');
out.push_str(word);
used = measure;
}
}
out
}
pub fn block(out: &mut String, indent: usize, label: &str, text: &str, width: usize) {
let cont = " ".repeat(indent + label.chars().count());
let mut opening = format!("{}{label}", " ".repeat(indent));
for paragraph in text.split('\n') {
let mut line = opening.clone();
let mut written = false;
for word in paragraph.split_whitespace() {
match written {
false => {
line.push_str(word);
written = true;
}
true => match line.chars().count() + 1 + word.chars().count() <= width {
true => {
line.push(' ');
line.push_str(word);
}
false => {
out.push_str(&line);
out.push('\n');
line = cont.clone();
line.push_str(word);
}
},
}
}
out.push_str(&line);
out.push('\n');
opening = cont.clone();
}
}
pub fn filled(text: &str, width: usize) -> String {
if text.is_empty() {
return String::new();
}
let ends = text.ends_with('\n');
let body = match ends {
true => &text[..text.len() - 1],
false => text,
};
let mut out: String = body
.split('\n')
.map(|line| one_indented(line, width))
.collect::<Vec<String>>()
.join("\n");
if ends {
out.push('\n');
}
out
}
fn one_indented(line: &str, width: usize) -> String {
if line.chars().count() <= width {
return line.to_string();
}
let indent = line.len() - line.trim_start_matches(' ').len();
let rest = &line[indent..];
if rest.trim().is_empty() {
return String::new();
}
if opens_past_the_room(line, width) {
return line.to_string();
}
let opening = " ".repeat(indent);
let cont = " ".repeat(indent + HANG);
let mut out = String::new();
let mut line_out = opening;
let mut written = false;
for word in rest.split_whitespace() {
match written {
false => {
line_out.push_str(word);
written = true;
}
true => match line_out.chars().count() + 1 + word.chars().count() <= width {
true => {
line_out.push(' ');
line_out.push_str(word);
}
false => {
out.push_str(&line_out);
out.push('\n');
line_out = cont.clone();
line_out.push_str(word);
}
},
}
}
out.push_str(&line_out);
out
}
const TAIL: usize = 2;
fn opens_past_the_room(line: &str, width: usize) -> bool {
let indent = line.len() - line.trim_start_matches(' ').len();
let mut words = line.split_whitespace();
let Some(first) = words.next() else {
return false;
};
let opening = indent + first.chars().count();
let tail: Vec<&str> = words.collect();
match tail.len() {
0 => opening > width,
1..=TAIL => {
opening
+ tail
.iter()
.map(|word| 1 + word.chars().count())
.sum::<usize>()
> width
}
_ => opening + 1 + tail[0].chars().count() > width,
}
}
pub fn unfoldable(line: &str, width: usize) -> bool {
opens_past_the_room(line, width)
}
#[cfg(test)]
mod tests {
use super::{block, filled, fold, fold_at, unfoldable, WIDEST, WIDTH};
#[test]
fn a_folded_line_is_never_wider_than_the_width() {
let text = "the manifest of the change this run is scoped to, and every line of it \
names one document the change carries";
for width in [20, 40, 70, 80] {
for line in fold(text, width).lines() {
assert!(
line.chars().count() <= width,
"{width}: {line:?} is {} columns",
line.chars().count()
);
}
}
assert_eq!(fold(text, 200), text);
}
#[test]
fn a_word_longer_than_the_width_is_never_broken() {
let text = "see docs/spec/06-engine-architecture.md#the-command-line for it";
let folded = fold(text, 20);
assert!(folded.contains("docs/spec/06-engine-architecture.md#the-command-line"));
assert_eq!(folded.split('\n').next(), Some("see"));
}
#[test]
fn a_break_the_author_wrote_survives_the_fold() {
assert_eq!(fold("one two\nthree four", 40), "one two\nthree four");
}
#[test]
fn the_suffix_that_follows_the_text_is_left_room_for() {
let width = 30;
let tail = "[default: 0]".chars().count();
let text = "the rotation seed, which is a member of the run identity";
let folded = fold_at(text, width, tail);
let last = folded.lines().last().expect("the fold wrote a line");
assert!(
last.chars().count() + 1 + tail <= width,
"{last:?} plus the suffix is past {width}"
);
assert_ne!(fold(text, width), folded);
}
#[test]
fn a_block_hangs_its_text_under_its_label() {
let mut out = String::new();
block(
&mut out,
2,
"fix: ",
"rewrite the sentence so that it states one claim and no more",
40,
);
let lines: Vec<&str> = out.trim_end().lines().collect();
assert_eq!(lines[0], " fix: rewrite the sentence so that it");
for line in &lines[1..] {
assert!(line.starts_with(&" ".repeat(7)), "{line:?}");
}
for line in &lines {
assert!(line.chars().count() <= 40, "{line:?}");
}
assert!(out.ends_with('\n'));
}
#[test]
fn a_report_that_already_fits_comes_back_unchanged() {
let report = "census\n 36 documents\n\n 4 excluded\nchecks\n 0 findings\n";
assert_eq!(filled(report, WIDTH), report);
}
#[test]
fn a_wrapped_line_keeps_its_indent_and_hangs_two_columns_in() {
let line = format!(" {}", "word ".repeat(30).trim_end());
let out = filled(&line, 40);
let lines: Vec<&str> = out.lines().collect();
assert!(lines.len() > 1, "the line wrapped");
assert!(lines[0].starts_with(" w"), "{:?}", lines[0]);
for one in &lines[1..] {
assert!(one.starts_with(" w"), "{one:?}");
}
for one in &lines {
assert!(one.chars().count() <= 40, "{one:?}");
}
}
#[test]
fn a_blank_line_stays_blank_and_the_closing_newline_survives() {
let text = "one\n\ntwo\n";
assert_eq!(filled(text, 10), text);
assert_eq!(filled("one\n\ntwo", 10), "one\n\ntwo");
assert_eq!(filled("", 10), "");
}
#[test]
fn a_line_whose_one_word_is_past_the_room_is_left_whole() {
let path = "docs/obligations/0146-the-stop-hook-reads-its-re-entry-guard.md";
let line = format!(" fix: add a heading to {path}");
let out = filled(&line, 40);
assert!(out.contains(path), "the path arrived intact: {out}");
let carrier = out
.lines()
.find(|one| one.contains(path))
.expect("a line carries the path");
assert_eq!(carrier.trim(), path, "the path is alone on its line");
assert!(unfoldable(carrier, 40), "that line names its own reason");
assert!(!unfoldable(" a short line", 40));
}
#[test]
fn a_line_that_opens_past_the_room_is_not_wrapped_after_it() {
let path = "docs/obligations/0146-the-stop-hook-reads-its-re-entry-guard.md";
let line = format!(" {path}:12:3 error");
assert_eq!(filled(&line, 40), line);
assert_eq!(filled(&format!(" {path}"), 40), format!(" {path}"));
}
#[test]
fn a_two_word_line_is_never_split_one_word_to_a_line() {
let width = 40;
for opening in 20..=48 {
let first = "p".repeat(opening - 2);
let line = format!(" {first} warn");
let out = filled(&line, width);
let lines: Vec<&str> = out.lines().collect();
assert!(
lines.len() == 1,
"at an opening of {opening} the line was split into {} lines:\n{out}",
lines.len()
);
assert!(
lines[0].ends_with(" warn"),
"at an opening of {opening} the severity left its line: {out:?}"
);
assert_eq!(
unfoldable(&line, width),
line.chars().count() > width && out == line,
"at an opening of {opening} the predicate and the fill disagree"
);
}
}
#[test]
fn a_finding_location_at_the_width_keeps_its_severity() {
let path = "docs/decisions/0041-q41-whether-vale-becomes-a-declared-regime-backend.md";
let opening = 2 + path.chars().count() + ":32:1".chars().count();
assert_eq!(opening, WIDTH, "this case is at the boundary it claims");
let line = format!(" {path}:32:1 error");
let out = filled(&line, WIDTH);
assert_eq!(out, line, "the severity left its location line:\n{out}");
assert_eq!(out.lines().count(), 1);
}
#[test]
fn a_glyph_never_rides_alone_when_its_severity_word_wraps() {
let width = 40;
for opening in 20..=48 {
let first = "p".repeat(opening - 2);
let line = format!(" {first} ▲ warn");
let out = filled(&line, width);
let lines: Vec<&str> = out.lines().collect();
assert!(
!lines.iter().any(|line| line.trim() == "warn"),
"at an opening of {opening} the severity word landed alone:\n{out}"
);
assert!(
!lines.iter().any(|line| line.trim() == "▲"),
"at an opening of {opening} the glyph landed alone:\n{out}"
);
assert_eq!(
unfoldable(&line, width),
line.chars().count() > width && out == line,
"at an opening of {opening} the predicate and the fill disagree"
);
}
}
#[test]
fn every_wide_line_the_fill_emits_names_itself() {
let report = " a short line\n \
docs/decisions/0041-q41-whether-vale-becomes-a-declared-regime-backend.md:32:1 error\n \
fix (mechanical): write `behavior` into \
docs/obligations/0146-the-stop-hook-reads-its-re-entry-guard-with-an-interpreter.md\n \
a much longer line of ordinary prose that will certainly need to be laid out at eighty columns\n";
for width in [40, WIDTH, WIDEST] {
let out = filled(report, width);
for line in out.lines() {
if line.chars().count() > width {
assert!(
unfoldable(line, width),
"at {width} the fill emitted a wide line it could have narrowed: {line:?}"
);
}
}
for line in out.lines() {
assert!(
!matches!(line.trim(), "error" | "warn" | "info"),
"at {width} a severity reached a line of its own:\n{out}"
);
}
}
}
#[test]
fn laying_out_a_laid_out_report_changes_nothing() {
let report = "checks\n a very long sentence that will certainly need to wrap at forty \
columns and then some more\n docs/one.md:1:1 warn\n";
let once = filled(report, 40);
assert_eq!(filled(&once, 40), once);
}
}