| ... | @@ -20,11 +20,14 @@ pub const Alignment = enum { | ... | @@ -20,11 +20,14 @@ pub const Alignment = enum { |
| 20 | right, | 20 | right, |
| 21 | }; | 21 | }; |
| 22 | | 22 | |
| | 23 | const default_alignment = .right; |
| | 24 | const default_fill_char = ' '; |
| | 25 | |
| 23 | pub const FormatOptions = struct { | 26 | pub const FormatOptions = struct { |
| 24 | precision: ?usize = null, | 27 | precision: ?usize = null, |
| 25 | width: ?usize = null, | 28 | width: ?usize = null, |
| 26 | alignment: Alignment = .right, | 29 | alignment: Alignment = default_alignment, |
| 27 | fill: u21 = ' ', | 30 | fill: u21 = default_fill_char, |
| 28 | }; | 31 | }; |
| 29 | | 32 | |
| 30 | /// Renders fmt string with args, calling `writer` with slices of bytes. | 33 | /// Renders fmt string with args, calling `writer` with slices of bytes. |
| ... | @@ -48,8 +51,8 @@ pub const FormatOptions = struct { | ... | @@ -48,8 +51,8 @@ pub const FormatOptions = struct { |
| 48 | /// | 51 | /// |
| 49 | /// Note that most of the parameters are optional and may be omitted. Also you can leave out separators like `:` and `.` when | 52 | /// Note that most of the parameters are optional and may be omitted. Also you can leave out separators like `:` and `.` when |
| 50 | /// all parameters after the separator are omitted. | 53 | /// all parameters after the separator are omitted. |
| 51 | /// Only exception is the *fill* parameter. If *fill* is required, one has to specify *alignment* as well, as otherwise | 54 | /// Only exception is the *fill* parameter. If a non-zero *fill* character is required at the same time as *width* is specified, |
| 52 | /// the digits after `:` is interpreted as *width*, not *fill*. | 55 | /// one has to specify *alignment* as well, as otherwise the digit following `:` is interpreted as *width*, not *fill*. |
| 53 | /// | 56 | /// |
| 54 | /// The *specifier* has several options for types: | 57 | /// The *specifier* has several options for types: |
| 55 | /// - `x` and `X`: output numeric value in hexadecimal notation | 58 | /// - `x` and `X`: output numeric value in hexadecimal notation |
| ... | @@ -239,29 +242,38 @@ pub const Placeholder = struct { | ... | @@ -239,29 +242,38 @@ pub const Placeholder = struct { |
| 239 | } | 242 | } |
| 240 | } | 243 | } |
| 241 | | 244 | |
| 242 | // Parse the fill character | 245 | // Parse the fill character, if present. |
| 243 | // The fill parameter requires the alignment parameter to be specified | 246 | // When the width field is also specified, the fill character must |
| 244 | // too | 247 | // be followed by an alignment specifier, unless it's '0' (zero) |
| 245 | const fill = comptime if (parser.peek(1)) |ch| | 248 | // (in which case it's handled as part of the width specifier) |
| | 249 | var fill: ?u21 = comptime if (parser.peek(1)) |ch| |
| 246 | switch (ch) { | 250 | switch (ch) { |
| 247 | '<', '^', '>' => parser.char().?, | 251 | '<', '^', '>' => parser.char(), |
| 248 | else => ' ', | 252 | else => null, |
| 249 | } | 253 | } |
| 250 | else | 254 | else |
| 251 | ' '; | 255 | null; |
| 252 | | 256 | |
| 253 | // Parse the alignment parameter | 257 | // Parse the alignment parameter |
| 254 | const alignment: Alignment = comptime if (parser.peek(0)) |ch| init: { | 258 | const alignment: ?Alignment = comptime if (parser.peek(0)) |ch| init: { |
| 255 | switch (ch) { | 259 | switch (ch) { |
| 256 | '<', '^', '>' => _ = parser.char(), | 260 | '<', '^', '>' => { |
| 257 | else => {}, | 261 | // consume the character |
| | 262 | break :init switch (parser.char().?) { |
| | 263 | '<' => .left, |
| | 264 | '^' => .center, |
| | 265 | else => .right, |
| | 266 | }; |
| | 267 | }, |
| | 268 | else => break :init null, |
| 258 | } | 269 | } |
| 259 | break :init switch (ch) { | 270 | } else null; |
| 260 | '<' => .left, | 271 | |
| 261 | '^' => .center, | 272 | // When none of the fill character and the alignment specifier have |
| 262 | else => .right, | 273 | // been provided, check whether the width starts with a zero. |
| 263 | }; | 274 | if (fill == null and alignment == null) { |
| 264 | } else .right; | 275 | fill = comptime if (parser.peek(0) == '0') '0' else null; |
| | 276 | } |
| 265 | | 277 | |
| 266 | // Parse the width parameter | 278 | // Parse the width parameter |
| 267 | const width = comptime parser.specifier() catch |err| | 279 | const width = comptime parser.specifier() catch |err| |
| ... | @@ -284,8 +296,8 @@ pub const Placeholder = struct { | ... | @@ -284,8 +296,8 @@ pub const Placeholder = struct { |
| 284 | | 296 | |
| 285 | return Placeholder{ | 297 | return Placeholder{ |
| 286 | .specifier_arg = cacheString(specifier_arg[0..specifier_arg.len].*), | 298 | .specifier_arg = cacheString(specifier_arg[0..specifier_arg.len].*), |
| 287 | .fill = fill, | 299 | .fill = fill orelse default_fill_char, |
| 288 | .alignment = alignment, | 300 | .alignment = alignment orelse default_alignment, |
| 289 | .arg = arg, | 301 | .arg = arg, |
| 290 | .width = width, | 302 | .width = width, |
| 291 | .precision = precision, | 303 | .precision = precision, |
| ... | @@ -2648,6 +2660,15 @@ test "sci float padding" { | ... | @@ -2648,6 +2660,15 @@ test "sci float padding" { |
| 2648 | try expectFmt("right-pad: 3.142e0****\n", "right-pad: {e:*<11.3}\n", .{number}); | 2660 | try expectFmt("right-pad: 3.142e0****\n", "right-pad: {e:*<11.3}\n", .{number}); |
| 2649 | } | 2661 | } |
| 2650 | | 2662 | |
| | 2663 | test "padding.zero" { |
| | 2664 | try expectFmt("zero-pad: '0042'", "zero-pad: '{:04}'", .{42}); |
| | 2665 | try expectFmt("std-pad: ' 42'", "std-pad: '{:10}'", .{42}); |
| | 2666 | try expectFmt("std-pad-1: '001'", "std-pad-1: '{:0>3}'", .{1}); |
| | 2667 | try expectFmt("std-pad-2: '911'", "std-pad-2: '{:1<03}'", .{9}); |
| | 2668 | try expectFmt("std-pad-3: ' 1'", "std-pad-3: '{:>03}'", .{1}); |
| | 2669 | try expectFmt("center-pad: '515'", "center-pad: '{:5^03}'", .{1}); |
| | 2670 | } |
| | 2671 | |
| 2651 | test "null" { | 2672 | test "null" { |
| 2652 | const inst = null; | 2673 | const inst = null; |
| 2653 | try expectFmt("null", "{}", .{inst}); | 2674 | try expectFmt("null", "{}", .{inst}); |