alignment fixes in contributions
This commit is contained in:
parent
cee557d55a
commit
23fd4e3b63
1 changed files with 93 additions and 31 deletions
|
|
@ -3274,13 +3274,10 @@ fn printReport(out: *std.Io.Writer, report: *const Report, label: []const u8, co
|
|||
try printSection(out, "Lot edits (same position, key rewritten - not counted)", color, h_color);
|
||||
for (report.changes) |c| switch (c.kind) {
|
||||
.lot_edited => {
|
||||
var buf: [256]u8 = undefined;
|
||||
const msg = std.fmt.bufPrint(
|
||||
&buf,
|
||||
" {s:<14}{s:<30} (strict key broke, shares unchanged)\n",
|
||||
.{ c.symbol, c.account },
|
||||
) catch " (lot edit)\n";
|
||||
try cli.printFg(out, color, mut_color, "{s}", .{msg});
|
||||
try cli.setFg(out, color, mut_color);
|
||||
try writeRowPrefix(out, c.symbol, c.account);
|
||||
try out.writeAll(" (strict key broke, shares unchanged)\n");
|
||||
try cli.reset(out, color);
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
|
|
@ -3344,10 +3341,10 @@ fn printReport(out: *std.Io.Writer, report: *const Report, label: []const u8, co
|
|||
total_cd_int += cd_int;
|
||||
|
||||
const acct_label = if (acct.len == 0) "(no account)" else acct;
|
||||
// 30 to match the per-change rows above. `Fidelity Emil 401(k) Roth BL`
|
||||
// is 28 characters, so 28 left zero gutter and the next field butted
|
||||
// straight up against the name.
|
||||
try out.print(" {s:<30}", .{acct_label});
|
||||
// Same width and the same gutter guarantee as the per-change rows, minus
|
||||
// the symbol column this section has no use for.
|
||||
try out.writeAll(" ");
|
||||
try padTo(out, acct_label, acct_w);
|
||||
try printSummaryCell(out, " new", t.new_money, color);
|
||||
try printSummaryCell(out, " drip", t.drip_confirmed, color);
|
||||
try printSummaryCell(out, " rollup", t.rollup, color);
|
||||
|
|
@ -3432,6 +3429,66 @@ fn printNone(out: *std.Io.Writer, color: bool, muted: [3]u8) !void {
|
|||
try cli.printFg(out, color, muted, " (none)\n", .{});
|
||||
}
|
||||
|
||||
/// Symbol column. 16 because `Engagement Ring` is 15 - illiquid assets carry
|
||||
/// free-form names rather than tickers, so the old 14 was sized for a population
|
||||
/// this report no longer only contains.
|
||||
const sym_w = 16;
|
||||
|
||||
/// Account column. 30 because `Fidelity Emil 401(k) Roth BL` is 28.
|
||||
const acct_w = 30;
|
||||
|
||||
/// Pad `s` into a `w`-wide field, always leaving at least one space behind it.
|
||||
///
|
||||
/// The guarantee is the point. `{s:<N}` OVERFLOWS instead of truncating, so a
|
||||
/// single over-long value both shifted its own row right AND consumed the gutter,
|
||||
/// welding itself to the next field. And no fixed `N` avoids it here: symbols in
|
||||
/// this report run from a 3-character ticker to a 24-character option
|
||||
/// (`AMZN 09/18/2026 280.00 C`), with hand-named illiquid assets in between. So
|
||||
/// the widths above are chosen for the common case and this keeps the rare long
|
||||
/// one readable - it loses its alignment, not its whitespace.
|
||||
fn padTo(out: *std.Io.Writer, s: []const u8, w: usize) !void {
|
||||
try out.writeAll(s);
|
||||
try out.splatByteAll(' ', if (s.len >= w) 1 else w - s.len);
|
||||
}
|
||||
|
||||
/// The ` symbol account ` prefix every per-change row opens with.
|
||||
///
|
||||
/// One function rather than a format-string literal repeated at fourteen call
|
||||
/// sites. The literal is how the widths drifted out of step in the first place:
|
||||
/// twelve rows said `{s:<14}{s:<24}`, the CD continuation said the same with
|
||||
/// blank arguments, the lot-edit row said `{s: <12} {s: <24}`, and the account
|
||||
/// summary said `{s:<28}` - four different answers to one question, and each new
|
||||
/// long value found a different one of them.
|
||||
///
|
||||
/// Callers own the single space that follows, so content lands one column past
|
||||
/// `acct_w`. Keep it: every section has to agree on that column or the report
|
||||
/// shears between sections instead of within a row, which is harder to spot.
|
||||
fn writeRowPrefix(out: *std.Io.Writer, symbol: []const u8, account: []const u8) !void {
|
||||
try out.writeAll(" ");
|
||||
try padTo(out, symbol, sym_w);
|
||||
try padTo(out, account, acct_w);
|
||||
}
|
||||
|
||||
test "padTo: pads short values to width and never welds a long one" {
|
||||
var buf: [128]u8 = undefined;
|
||||
|
||||
// Short: padded to the column.
|
||||
var w1 = std.Io.Writer.fixed(&buf);
|
||||
try padTo(&w1, "CASH", 16);
|
||||
try std.testing.expectEqualStrings("CASH ", w1.buffered());
|
||||
|
||||
// Exactly one under: still a gutter, and it is the last width that aligns.
|
||||
var w2 = std.Io.Writer.fixed(&buf);
|
||||
try padTo(&w2, "Engagement Ring", 16);
|
||||
try std.testing.expectEqualStrings("Engagement Ring ", w2.buffered());
|
||||
|
||||
// Over: alignment is gone, whitespace is not. `{s:<16}` produced no space
|
||||
// here at all, which ran the value into the next column.
|
||||
var w3 = std.Io.Writer.fixed(&buf);
|
||||
try padTo(&w3, "AMZN 09/18/2026 280.00 C", 16);
|
||||
try std.testing.expectEqualStrings("AMZN 09/18/2026 280.00 C ", w3.buffered());
|
||||
}
|
||||
|
||||
fn printTotalLine(out: *std.Io.Writer, label: []const u8, v: f64, color: bool, hdr: [3]u8) !void {
|
||||
try cli.printFg(out, color, hdr, " {s}: {f}\n", .{ label, Money.from(v) });
|
||||
}
|
||||
|
|
@ -3445,7 +3502,7 @@ fn printChangeLine(out: *std.Io.Writer, c: Change, color: bool, pos: [3]u8) !voi
|
|||
const val_str = std.fmt.bufPrint(&val_buf, "{f}", .{Money.from(c.value())}) catch "$?";
|
||||
|
||||
const acct = if (c.account.len == 0) "(no account)" else c.account;
|
||||
try out.print(" {s:<14}{s:<30}", .{ c.symbol, acct });
|
||||
try writeRowPrefix(out, c.symbol, acct);
|
||||
if (c.security_type == .cash) {
|
||||
try cli.printFg(out, color, pos, " {s}", .{val_str});
|
||||
} else {
|
||||
|
|
@ -3464,15 +3521,15 @@ fn printCdLine(out: *std.Io.Writer, c: Change, implied_interest: ?f64, color: bo
|
|||
.cd_removed_early => "removed EARLY",
|
||||
else => "removed",
|
||||
};
|
||||
try out.print(" {s:<14}{s:<30} {s:<16} face {f} maturity {s}\n", .{
|
||||
c.symbol,
|
||||
acct,
|
||||
try writeRowPrefix(out, c.symbol, acct);
|
||||
try out.print(" {s:<16} face {f} maturity {s}\n", .{
|
||||
verb,
|
||||
Money.from(c.face_value),
|
||||
mat_str,
|
||||
});
|
||||
if (implied_interest) |i| {
|
||||
try cli.printFg(out, color, cli.CLR_POSITIVE, " {s:<14}{s:<30} implied interest: {f}\n", .{ "", "", Money.from(i) });
|
||||
try writeRowPrefix(out, "", "");
|
||||
try cli.printFg(out, color, cli.CLR_POSITIVE, " implied interest: {f}\n", .{Money.from(i)});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3480,7 +3537,8 @@ fn printCashDeltaLine(out: *std.Io.Writer, c: Change, report: *const Report, col
|
|||
const v = c.value();
|
||||
const acct = if (c.account.len == 0) "(no account)" else c.account;
|
||||
const sign = if (v >= 0) "+" else "-";
|
||||
try out.print(" {s:<14}{s:<30} cash ", .{ c.symbol, acct });
|
||||
try writeRowPrefix(out, c.symbol, acct);
|
||||
try out.writeAll(" cash ");
|
||||
try cli.printGainLoss(out, color, v, "{s}{f}", .{ sign, Money.from(@abs(v)) });
|
||||
|
||||
// Hint if a CD matured in the same account.
|
||||
|
|
@ -3495,9 +3553,9 @@ fn printCashDeltaLine(out: *std.Io.Writer, c: Change, report: *const Report, col
|
|||
|
||||
fn printPriceOnlyLine(out: *std.Io.Writer, c: Change, color: bool, muted: [3]u8) !void {
|
||||
const acct = if (c.account.len == 0) "(no account)" else c.account;
|
||||
try cli.printFg(out, color, muted, " {s:<14}{s:<30} price {f} -> {f}\n", .{
|
||||
c.symbol,
|
||||
acct,
|
||||
try cli.setFg(out, color, muted);
|
||||
try writeRowPrefix(out, c.symbol, acct);
|
||||
try out.print(" price {f} -> {f}\n", .{
|
||||
Money.from(c.old_price),
|
||||
Money.from(c.new_price),
|
||||
});
|
||||
|
|
@ -3508,17 +3566,18 @@ fn printFlaggedLine(out: *std.Io.Writer, c: Change, color: bool, warn: [3]u8) !v
|
|||
try cli.setFg(out, color, warn);
|
||||
switch (c.kind) {
|
||||
.flagged => {
|
||||
try out.print(" {s:<14}{s:<30} {s}", .{ c.symbol, acct, c.detail orelse "edited" });
|
||||
try writeRowPrefix(out, c.symbol, acct);
|
||||
try out.print(" {s}", .{c.detail orelse "edited"});
|
||||
},
|
||||
.lot_removed => {
|
||||
try out.print(" {s:<14}{s:<30} {s} lot removed (face {f})", .{
|
||||
c.symbol, acct, @tagName(c.security_type), Money.from(c.face_value),
|
||||
try writeRowPrefix(out, c.symbol, acct);
|
||||
try out.print(" {s} lot removed (face {f})", .{
|
||||
@tagName(c.security_type), Money.from(c.face_value),
|
||||
});
|
||||
},
|
||||
.drip_negative => {
|
||||
try out.print(" {s:<14}{s:<30} shares decreased on existing lot ({f})", .{
|
||||
c.symbol, acct, Money.from(@abs(c.value())),
|
||||
});
|
||||
try writeRowPrefix(out, c.symbol, acct);
|
||||
try out.print(" shares decreased on existing lot ({f})", .{Money.from(@abs(c.value()))});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
|
@ -3637,7 +3696,7 @@ fn printPartialTransferLine(out: *std.Io.Writer, c: Change, color: bool, pos: [3
|
|||
const lot_value = c.value();
|
||||
const sym = if (c.symbol.len > 0) c.symbol else "cash";
|
||||
|
||||
try out.print(" {s:<14}{s:<30}", .{ sym, acct });
|
||||
try writeRowPrefix(out, sym, acct);
|
||||
try cli.printFg(out, color, pos, " {f}", .{Money.from(residual)});
|
||||
try cli.printFg(
|
||||
out,
|
||||
|
|
@ -3659,7 +3718,7 @@ fn printCashFundedResidualLine(out: *std.Io.Writer, c: Change, color: bool, pos:
|
|||
const lot_value = c.value();
|
||||
const sym = if (c.symbol.len > 0) c.symbol else "cash";
|
||||
|
||||
try out.print(" {s:<14}{s:<30}", .{ sym, acct });
|
||||
try writeRowPrefix(out, sym, acct);
|
||||
try cli.printFg(out, color, pos, " {f}", .{Money.from(residual)});
|
||||
try cli.printFg(
|
||||
out,
|
||||
|
|
@ -3724,9 +3783,11 @@ fn printCollapsedSales(out: *std.Io.Writer, report: *const Report, color: bool,
|
|||
// current-price proxy, so say which one the reader is looking at.
|
||||
const basis: []const u8 = if (closed_in_place) "at close" else "at mark";
|
||||
if (lots == 1) {
|
||||
try out.print(" {s:<14}{s:<30} sold {s} ({f})", .{ c.symbol, acct, basis, Money.from(proceeds) });
|
||||
try writeRowPrefix(out, c.symbol, acct);
|
||||
try out.print(" sold {s} ({f})", .{ basis, Money.from(proceeds) });
|
||||
} else {
|
||||
try out.print(" {s:<14}{s:<30} sold {d} lots {s} ({f})", .{ c.symbol, acct, lots, basis, Money.from(proceeds) });
|
||||
try writeRowPrefix(out, c.symbol, acct);
|
||||
try out.print(" sold {d} lots {s} ({f})", .{ lots, basis, Money.from(proceeds) });
|
||||
}
|
||||
try cli.reset(out, color);
|
||||
try out.writeAll("\n");
|
||||
|
|
@ -3744,7 +3805,8 @@ fn printInternalPurchaseLine(out: *std.Io.Writer, c: Change, color: bool, muted:
|
|||
const lot_value = c.value();
|
||||
|
||||
try cli.setFg(out, color, muted);
|
||||
try out.print(" {s:<14}{s:<30} {f} from existing cash", .{ sym, acct, Money.from(c.internal_funded) });
|
||||
try writeRowPrefix(out, sym, acct);
|
||||
try out.print(" {f} from existing cash", .{Money.from(c.internal_funded)});
|
||||
if (c.internal_funded + 0.005 < lot_value) {
|
||||
try out.print(" (of {f} lot)", .{Money.from(lot_value)});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue