chiark / gitweb /
LitLinesCrlf rework
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 10 May 2026 10:22:24 +0000 (11:22 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 10 May 2026 11:06:46 +0000 (12:06 +0100)
Use the indentation of the first nonempty line, rather than trimming
all whitespace from the front of every line.  That will let us do
indented lines if we want.

Cons rather less.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
client/client.rs

index 04686ebd6d04c5ba5ea9b0f9f3786326c3bba924..8251d98ecb99452f24121b0d17b2357963a05289 100644 (file)
@@ -29,20 +29,35 @@ pub struct LitLinesCrlf<'s>(pub &'s str);
 
 impl Display for LitLinesCrlf<'_> {
   fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-    let input = &self.0;
-
-  // TODO don't collect
-  // TODO more principled indentation handling
-  let output = input.split_inclusive('\n')
-    .map(|s| s.trim_start_matches(&[' ','\t'][..]))
-    .map(|s| match s.strip_suffix("\n") {
-      None => [s, ""],
-      Some(l) => [l, "\r\n"],
-    })
-    .flatten()
-    .collect::<String>();
-
-    write!(f, "{output}")
+    let input = self.0.split_inclusive('\n');
+
+    // Use indentation from first nonempty line
+    let indent = input.clone()
+      .filter_map(|l| {
+        let trimmed = l.trim_start();
+        if trimmed.is_empty() {
+          None
+        } else {
+          Some(l.len() - trimmed.len())
+        }
+      })
+      .next()
+      .unwrap_or(0);
+
+    for l in input {
+      let (l, nl) = l.strip_suffix("\n")
+        .map(|l| (l, Some(())))
+        .unwrap_or((l, None));
+
+      let (spc, content) = l.split_at_checked(indent).unwrap_or((l, ""));
+      assert!(spc.trim().is_empty(), "non-blank indent {:?} in {:?}", spc, l);
+      write!(f, "{content}")?;
+      if let Some(()) = nl {
+        write!(f, "\r\n")?;
+      }
+    }
+
+    Ok(())
   }
 }