From 16bfbc7ac0562eb88648c681d741155fd353ac11 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 23:24:31 +0000 Subject: [PATCH] [patch] Keep a gap after a long label in --status and align every value StatusReport padded each label to a fixed 15 columns, so a label of 14 or more characters had its value glued to the colon. The value column is now sized from the longest label printed, with at least one space after it. Fixes #75 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017vfJW5JsJWSEGD7D4Jhs1d --- TrayApp.Test/StatusReportTests.cs | 26 +++++++++++++++++++++++++ TrayApp/StatusReport.cs | 32 ++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/TrayApp.Test/StatusReportTests.cs b/TrayApp.Test/StatusReportTests.cs index bac994a..9ab8d49 100644 --- a/TrayApp.Test/StatusReportTests.cs +++ b/TrayApp.Test/StatusReportTests.cs @@ -47,6 +47,32 @@ public void Build_WithAStatusAndDetails_AppendsThemInOrder() Assert.Contains("systemd-inhibit", report, StringComparison.Ordinal); } + [TestMethod] + public void Build_WithALongDetailLabel_KeepsAGapAndAlignsEveryValue() + { + string report = StatusReport.Build( + "demo", + "on", + [new StatusDetail("Power assertion", () => "held"), new StatusDetail("Inhibitor path", () => "/usr/bin/systemd-inhibit")]); + + Assert.Contains("Power assertion: ", report, StringComparison.Ordinal); + Assert.Contains("Inhibitor path: ", report, StringComparison.Ordinal); + + string[] rows = report.Split('\n')[1..]; + int column = rows[0].IndexOf(':', StringComparison.Ordinal) + 1; + while (rows[0][column] == ' ') + { + column++; + } + + foreach (string row in rows) + { + string line = row.TrimEnd('\r'); + Assert.AreEqual(' ', line[column - 1], $"Row \"{line}\" has no gap before its value column."); + Assert.AreNotEqual(' ', line[column], $"Row \"{line}\" does not start its value at column {column}."); + } + } + [TestMethod] public void Build_EndsWithoutATrailingNewline() { diff --git a/TrayApp/StatusReport.cs b/TrayApp/StatusReport.cs index 039d9d5..9a72111 100644 --- a/TrayApp/StatusReport.cs +++ b/TrayApp/StatusReport.cs @@ -19,6 +19,7 @@ namespace ktsu.TrayApp; public static class StatusReport { private const int LabelWidth = 15; + private const int MinimumGap = 1; /// /// Describes what a tool can do on this machine. @@ -33,26 +34,39 @@ public static string Build(string name, string? status, IReadOnlyList rows = + [ + ("Platform", string.Create(CultureInfo.InvariantCulture, $"{RuntimeInformation.OSDescription.Trim()} ({RuntimeInformation.RuntimeIdentifier})")), + ("Tray icon", DesktopSession.Explanation), + ]; if (status is not null) { - AppendRow(report, "State", status); + rows.Add(("State", status)); } foreach (StatusDetail detail in details) { - AppendRow(report, detail.Label, detail.Value()); + rows.Add((detail.Label, detail.Value())); + } + + // The value column starts after the longest label, so a long label still has a gap before its value + // and every value lines up with the others. + int labelWidth = LabelWidth; + foreach ((string label, _) in rows) + { + labelWidth = Math.Max(labelWidth, label.Length + MinimumGap + 1); + } + + StringBuilder report = new(); + report.AppendLine(HelpText.Version(name)); + foreach ((string label, string value) in rows) + { + report.AppendLine($"{(label + ':').PadRight(labelWidth)}{value}"); } // Every row above appended a newline; the report is quoted and compared in tests, so it ends where // the last row's text ends. return report.ToString().TrimEnd('\r', '\n'); } - - private static void AppendRow(StringBuilder report, string label, string value) => - report.AppendLine($"{(label + ':').PadRight(LabelWidth)}{value}"); }