diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d162c2..6132f39 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,11 +8,11 @@ of Ziglings Maintenance! Ziglings is intended for programmers of all experience levels. No specific language knowledge is expected. Anyone who can install -the current Zig snapshot, setup a copy of Ziglings, and knows +the current Zig snapshot, set up a copy of Ziglings, and knows common language building blocks (if/then/else, loops, and functions) is ready for Ziglings. -Zigling's excercises are self-contained. If you can't solve +Zigling's exercises are self-contained. If you can't solve an exercise from the information you've gleaned so far from Ziglings, then the exercise probably needs some additional work. Please file an issue! @@ -95,7 +95,7 @@ interface. Specifically: eternal Ziglings contributor glory is yours! -## Licence +## License If you submit your contribution to the repository/project, you agree that your contribution will be licensed under diff --git a/build.zig b/build.zig index afc1021..1dfc7be 100644 --- a/build.zig +++ b/build.zig @@ -81,6 +81,7 @@ pub fn build(b: *Build) !void { const run = b.addRunArtifact(elrond); run.addArg(b.fmt("--zig={s}", .{b.graph.zig_exe})); run.addArg(b.fmt("--work-path={s}", .{work_path})); + run.addArg(b.fmt("--root-path={s}", .{b.root.root_dir.path.?})); if (exno) |n| { run.addArg(b.fmt("--only={d}", .{n})); diff --git a/exercises/005_arrays2.zig b/exercises/005_arrays2.zig index 07b9abe..1de9a36 100644 --- a/exercises/005_arrays2.zig +++ b/exercises/005_arrays2.zig @@ -26,8 +26,13 @@ pub fn main() void { // (Problem 2) // Please set this array using repetition. // It should result in: 1 0 0 1 1 0 0 1 1 0 0 1 - const bit_pattern_unit = [_]u8{ ??? }; - const bit_pattern: [3 * bit_pattern_unit.len]u8 = @bitCast(@as([3][bit_pattern_unit.len]u8, @splat(bit_pattern_unit))); + const bit_pattern_unit = ???; + + // How long should the bit pattern be? + const len = ???; + + // For now, don't worry about the use of SIMD. + const bit_pattern: [len]u8 = std.simd.repeat(len, bit_pattern_unit); // Okay, that's all of the problems. Let's see the results. // @@ -53,3 +58,12 @@ pub fn main() void { std.debug.print("\n", .{}); } + +// For the curious: +// +// The `std.simd.repeat` function takes a target length and a pattern, +// and returns a vector filled with that pattern repeated to the +// desired length. +// +// For example, `repeat(5, [_]u8{1, 2})` will return a vector +// equivalent to `.{1, 2, 1, 2, 1}`. diff --git a/exercises/043_pointers5.zig b/exercises/043_pointers5.zig index 9e2fa6f..3117639 100644 --- a/exercises/043_pointers5.zig +++ b/exercises/043_pointers5.zig @@ -1,6 +1,6 @@ // // As with integers, you can pass a pointer to a struct when you -// will wish to modify that struct. Pointers are also useful when +// wish to modify that struct. Pointers are also useful when // you need to store a reference to a struct (a "link" to it). // // const Vertex = struct{ x: u32, y: u32, z: u32 }; diff --git a/exercises/047_methods.zig b/exercises/047_methods.zig index 3221ca2..3e88719 100644 --- a/exercises/047_methods.zig +++ b/exercises/047_methods.zig @@ -37,6 +37,24 @@ // self, others use a lowercase version of the type name, but feel // free to use whatever is most appropriate. // +// "But hold on," you say, eyeing a() and b() suspiciously, "why +// does one take 'self' and another take '*self'?" Sharp eye! +// +// It all comes down to a single question: does the function need +// to CHANGE the struct? +// +// * Needs to change it? Take a pointer (*Bar). Without it you'd +// be scribbling on a COPY, and your changes would evaporate the +// instant the function returns. Poof. +// * Only reads it? Plain Bar is just fine. +// (For a big, bulky struct you might still write *const Bar to +// avoid copying it around, but for small ones a copy is cheap.) +// +// You'll see this below: zap() takes 'self: HeatRay' by value +// because it only reads the ray's damage, but it takes the alien +// as '*Alien' because zapping is supposed to HURT - and that means +// changing the alien's health for real, not on a throwaway copy. +// // Okay, you're armed. // // Now, please zap the alien structs until they're all gone or diff --git a/exercises/065_builtins2.zig b/exercises/065_builtins2.zig index 78cd23b..9d137ef 100644 --- a/exercises/065_builtins2.zig +++ b/exercises/065_builtins2.zig @@ -47,7 +47,7 @@ const Narcissus = struct { myself: *Narcissus = undefined, echo: void = undefined, // Alas, poor Echo! - fn fetchTheMostBeautifulType() type { + fn FetchTheMostBeautifulType() type { return @This(); } }; @@ -70,7 +70,7 @@ pub fn main() void { // // The fix for this is very subtle, but it makes a big // difference! - const Type2 = narcissus.fetchTheMostBeautifulType(); + const Type2 = narcissus.FetchTheMostBeautifulType(); // Now we print a pithy statement about Narcissus. print("A {s} loves all {s}es. ", .{ @@ -94,7 +94,7 @@ pub fn main() void { print("He has room in his heart for:", .{}); // `field_names` is a slice of strings and it holds the names of the struct's fields - // `field_types` is a slice of strings and it holds the types of the struct's fields, + // `field_types` is a slice of types and it holds the types of the struct's fields, // it is guaranteed to be the same length as `field_names` const field_names = @typeInfo(Narcissus).@"struct".field_names; const field_types = @typeInfo(Narcissus).@"struct".field_types; diff --git a/exercises/107_threading.zig b/exercises/107_threading.zig index 3c3fa21..aaec6b1 100644 --- a/exercises/107_threading.zig +++ b/exercises/107_threading.zig @@ -1,5 +1,5 @@ // -// In Exercises 84-91, we learned about Zig's Io interface for +// In Exercises 85-94 and quiz 95, we learned about Zig's Io interface for // concurrent execution: io.async(), Group, Select, and Futures. // Under the hood, the Threaded backend manages a pool of real // OS threads for you - including scheduling, cancellation, and @@ -74,11 +74,11 @@ pub fn main() !void { // before the parallel processing begins. std.debug.print("Starting work...\n", .{}); - // These curly brackets are very important, they are necessary - // to enclose the area where the threads are called. - // Without these brackets, the program would not wait for the - // end of the threads and they would continue to run beyond the - // end of the program. + // These curly braces are very important, they are necessary + // to enclose the area where the threads are called and joined. + // With these braces, the program will block and wait for all threads + // to finish right at the closing brace of this block, ensuring + // "Zig is cool!" is always printed last. { // Now we start the first thread, with the number as parameter const handle = try std.Thread.spawn(.{}, thread_function, .{1}); @@ -102,8 +102,7 @@ pub fn main() !void { try io.sleep(std.Io.Duration.fromMilliseconds(400), .awake); std.debug.print("Some weird stuff, after starting the threads.\n", .{}); } - // After we have left the closed area, we wait until - // the threads have run through, if this has not yet been the case. + // The threads are guaranteed to be finished by the time we reach here. std.debug.print("Zig is cool!\n", .{}); } diff --git a/exercises/108_threading2.zig b/exercises/108_threading2.zig index e731a2a..f143b40 100644 --- a/exercises/108_threading2.zig +++ b/exercises/108_threading2.zig @@ -46,7 +46,7 @@ // 1,000,000,000 partial values. And for each additional digit we have to // add a zero. // Even fast computers - and I mean really fast computers - get a bit warmer -// on the CPU when it comes to really many digits. But the 8 digits are +// on the CPU when it comes to a large number of digits. But 8 digits are // enough for us for now, because we want to understand the principle and // nothing more, right? // diff --git a/exercises/109_files.zig b/exercises/109_files.zig index bf4de9b..5514c43 100644 --- a/exercises/109_files.zig +++ b/exercises/109_files.zig @@ -50,7 +50,7 @@ pub fn main(init: std.process.Init) !void { // wait a minute... // opening a directory might fail! // what should we do here? - var output_dir: std.Io.Dir = try cwd.openDir(io, "output", .{}); + var output_dir: std.Io.Dir = cwd.openDir(io, "output", .{}); defer output_dir.close(io); // we try to open the file `zigling.txt`, diff --git a/exercises/112_vectors.zig b/exercises/112_vectors.zig index 748c086..0c43f9c 100644 --- a/exercises/112_vectors.zig +++ b/exercises/112_vectors.zig @@ -145,3 +145,7 @@ pub fn main() void { print("Max difference (old fn): {d: >5.3}\n", .{mpd_old}); print("Max difference (new fn): {d: >5.3}\n", .{mpd_new}); } + +// Another cool feature of Vectors is repeating patterns. +// Remember the arrays exercise from earlier where we created an array +// by repeating a pattern? See `005_arrays2.zig`. diff --git a/exercises/113_quiz9.zig b/exercises/113_quiz9.zig index 8f5cb61..8d1da9c 100644 --- a/exercises/113_quiz9.zig +++ b/exercises/113_quiz9.zig @@ -14,7 +14,7 @@ // // A common activity in microcontroller programming is setting and clearing // bits on input and output pins. This lets you control LEDs, sensors, motors -// and more! In a previous exercise (097_bit_manipulation.zig) you learned how +// and more! In a previous exercise (100_bit_manipulation.zig) you learned how // to swap two bytes using the ^ (XOR - exclusive or) operator. This quiz will // test your knowledge of bit manipulation in Zig while giving you a taste of // what it's like to control registers in a real microcontroller. Included at diff --git a/exercises/114_packed.zig b/exercises/114_packed.zig index 933ae0a..74ec7a1 100644 --- a/exercises/114_packed.zig +++ b/exercises/114_packed.zig @@ -1,6 +1,6 @@ // // We've already learned plenty about bit manipulation using bitwise operations -// in exercises 097 and 098 and in quiz 110. The techniques we already know work +// in exercises 100 and 101 and in quiz 113. The techniques we already know work // just fine, but creating masks and shifting individual bits around can become // quite tedious and unwieldy pretty quickly. // What if there was a better, a more convenient way to control individual bits? @@ -78,7 +78,7 @@ const FLG = packed struct(u8) { content_checksum: bool, content_size: bool, block_checksum: bool, - block_indepencence: bool, + block_independence: bool, version: u2, }; diff --git a/exercises/115_packed2.zig b/exercises/115_packed2.zig index 2b6b558..2a772d7 100644 --- a/exercises/115_packed2.zig +++ b/exercises/115_packed2.zig @@ -1,5 +1,5 @@ // -// We've already learned about switch statements in exercises 030, 031 and 108. +// We've already learned about switch statements in exercises 030, 031 and 111. // They also work with packed containers: const S = packed struct(u2) { @@ -51,11 +51,11 @@ comptime { // // Try to make the float below negative: -/// IEEE 754 half precision float +// IEEE 754 binary16 floating-point format const Float = packed union(u16) { value: f16, bits: packed struct(u16) { - mantissa: u10, + significand: u10, exponent: u5, sign: u1, }, diff --git a/patches/patches/005_arrays2.patch b/patches/patches/005_arrays2.patch index d1abb36..0c72bd7 100644 --- a/patches/patches/005_arrays2.patch +++ b/patches/patches/005_arrays2.patch @@ -1,6 +1,6 @@ ---- exercises/005_arrays2.zig 2026-05-04 16:26:32.778330847 +0200 -+++ answers/005_arrays2.zig 2026-05-04 16:26:13.082917974 +0200 -@@ -21,12 +21,12 @@ +--- exercises/005_arrays2.zig 2026-06-27 09:03:29.918124920 +0000 ++++ answers/005_arrays2.zig 2026-06-27 09:01:13.162138431 +0000 +@@ -21,15 +21,15 @@ // (Problem 1) // Please set this array concatenating the two arrays above. // It should result in: 1 3 3 7 @@ -10,8 +10,12 @@ // (Problem 2) // Please set this array using repetition. // It should result in: 1 0 0 1 1 0 0 1 1 0 0 1 -- const bit_pattern_unit = [_]u8{ ??? }; +- const bit_pattern_unit = ???; + const bit_pattern_unit = [_]u8{ 1, 0, 0, 1 }; - const bit_pattern: [3 * bit_pattern_unit.len]u8 = @bitCast(@as([3][bit_pattern_unit.len]u8, @splat(bit_pattern_unit))); - // Okay, that's all of the problems. Let's see the results. + // How long should the bit pattern be? +- const len = ???; ++ const len = 3 * bit_pattern_unit.len; + + // For now, don't worry about the use of SIMD. + const bit_pattern: [len]u8 = std.simd.repeat(len, bit_pattern_unit); diff --git a/patches/patches/047_methods.patch b/patches/patches/047_methods.patch index a7e8a26..4755ecb 100644 --- a/patches/patches/047_methods.patch +++ b/patches/patches/047_methods.patch @@ -1,6 +1,6 @@ ---- exercises/047_methods.zig 2023-10-03 22:15:22.122241138 +0200 -+++ answers/047_methods.zig 2023-10-05 20:04:07.056100214 +0200 -@@ -88,7 +88,7 @@ +--- exercises/047_methods.zig 2026-06-21 16:11:59.110876971 +0200 ++++ answers/047_methods.zig 2026-06-21 16:13:07.066363558 +0200 +@@ -106,7 +106,7 @@ for (&aliens) |*alien| { // *** Zap the alien with the heat ray here! *** diff --git a/patches/patches/065_builtins2.patch b/patches/patches/065_builtins2.patch index c011646..2abbc6f 100644 --- a/patches/patches/065_builtins2.patch +++ b/patches/patches/065_builtins2.patch @@ -13,8 +13,8 @@ // // The fix for this is very subtle, but it makes a big // difference! -- const Type2 = narcissus.fetchTheMostBeautifulType(); -+ const Type2 = Narcissus.fetchTheMostBeautifulType(); +- const Type2 = narcissus.FetchTheMostBeautifulType(); ++ const Type2 = Narcissus.FetchTheMostBeautifulType(); // Now we print a pithy statement about Narcissus. print("A {s} loves all {s}es. ", .{ diff --git a/patches/patches/109_files.patch b/patches/patches/109_files.patch index ac59d70..c8ad47c 100644 --- a/patches/patches/109_files.patch +++ b/patches/patches/109_files.patch @@ -1,5 +1,5 @@ ---- exercises/109_files.zig 2026-03-20 19:23:48.874150121 +0100 -+++ answers/109_files.zig 2026-04-02 10:51:15.813831695 +0200 +--- exercises/109_files.zig 2026-06-29 17:08:04.124224900 +0000 ++++ answers/109_files.zig 2026-06-29 17:13:20.696220533 +0000 @@ -41,7 +41,7 @@ // by doing nothing // @@ -9,6 +9,15 @@ // if there's any other unexpected error we just propagate it through else => return e, }; +@@ -50,7 +50,7 @@ + // wait a minute... + // opening a directory might fail! + // what should we do here? +- var output_dir: std.Io.Dir = cwd.openDir(io, "output", .{}); ++ var output_dir: std.Io.Dir = try cwd.openDir(io, "output", .{}); + defer output_dir.close(io); + + // we try to open the file `zigling.txt`, @@ -61,7 +61,7 @@ // but here we are not yet done writing to the file // if only there were a keyword in Zig that diff --git a/rivendell/elrond.zig b/rivendell/elrond.zig index 0d98255..f9ca0c4 100644 --- a/rivendell/elrond.zig +++ b/rivendell/elrond.zig @@ -19,12 +19,28 @@ const std = @import("std"); const builtin = @import("builtin"); +const Io = std.Io; +const File = Io.File; const Process = std.process; +const Terminal = std.Io.Terminal; const print = std.debug.print; const cutPrefix = std.mem.cutPrefix; const progress_filename = ".progress.txt"; +const no_colors_help = + \\Colors are not available. Check the NO_COLOR environ variable. + \\ + \\ +; + +const windows_colors_help = + \\Windows colors are not supported. + \\Install Windows Terminal or update to a more recent Windows version. + \\ + \\ +; + pub const logo = \\ _ _ _ \\ ___(_) __ _| (_)_ __ __ _ ___ @@ -109,41 +125,23 @@ const Context = struct { arena: std.mem.Allocator, zig_exe: []const u8, work_path: []const u8, + root_path: []const u8, }; const Error = error{Failed}; -// Ansi colors. -var use_color_escapes = false; -var red_text: []const u8 = ""; -var red_bold_text: []const u8 = ""; -var red_dim_text: []const u8 = ""; -var green_text: []const u8 = ""; -var yellow_text: []const u8 = ""; -var bold_text: []const u8 = ""; -var reset_text: []const u8 = ""; +var stderr_term_mode: Terminal.Mode = .no_color; -fn setupColors(io: std.Io) void { - use_color_escapes = false; - const stderr = std.Io.File.stderr(); - if (stderr.supportsAnsiEscapeCodes(io)) |ok| { - if (ok) use_color_escapes = true; - } else |_| {} - if (!use_color_escapes and builtin.os.tag == .windows) { - if (stderr.enableAnsiEscapeCodes(io)) { - use_color_escapes = true; - } else |_| {} - } - if (use_color_escapes) { - red_text = "\x1b[31m"; - red_bold_text = "\x1b[31;1m"; - red_dim_text = "\x1b[31;2m"; - green_text = "\x1b[32m"; - yellow_text = "\x1b[33m"; - bold_text = "\x1b[1m"; - reset_text = "\x1b[0m"; - } -} +// Ansi colors. +const C = struct { + var red: []const u8 = "\x1b[31m"; + var red_bold: []const u8 = "\x1b[31;1m"; + var red_dim: []const u8 = "\x1b[31;2m"; + var green: []const u8 = "\x1b[32m"; + var yellow: []const u8 = "\x1b[33m"; + var bold: []const u8 = "\x1b[1m"; + var reset: []const u8 = "\x1b[0m"; +}; pub fn main(init: std.process.Init) !void { const io = init.io; @@ -152,24 +150,51 @@ pub fn main(init: std.process.Init) !void { var args_it = try init.minimal.args.iterateAllocator(arena); if (!args_it.skip()) @panic("expected self arg"); - setupColors(io); + const stderr = File.stderr(); + + // Setup colors. + const NO_COLOR = try init.minimal.environ.containsUnempty(arena, "NO_COLOR"); + const CLICOLOR_FORCE = try init.minimal.environ.containsUnempty(arena, "CLICOLOR_FORCE"); + + const term_mode = try Terminal.Mode.detect(io, stderr, NO_COLOR, CLICOLOR_FORCE); + stderr_term_mode = term_mode; + + // Show help in case colors are not available or not supported. + switch (term_mode) { + .no_color => print(no_colors_help, .{}), + .escape_codes => {}, + .windows_api => print(windows_colors_help, .{}), + } + + if (term_mode == .no_color) { + C.red = ""; + C.red_bold = ""; + C.red_dim = ""; + C.green = ""; + C.yellow = ""; + C.bold = ""; + C.reset = ""; + } if (!validateExercises()) std.process.exit(1); var zig_exe: []const u8 = "zig"; var work_path: []const u8 = "exercises"; + var root_path: []const u8 = ""; var mode: Mode = .normal; var only_n: ?usize = null; var start_n: ?usize = null; while (args_it.next()) |arg| { if (std.mem.eql(u8, arg, "--logo")) { - print("{s}{s}{s}", .{ yellow_text, logo, reset_text }); + print("{s}{s}{s}", .{ C.yellow, logo, C.reset }); return; } else if (cutPrefix(u8, arg, "--zig=")) |v| { zig_exe = v; } else if (cutPrefix(u8, arg, "--work-path=")) |v| { work_path = v; + } else if (cutPrefix(u8, arg, "--root-path=")) |v| { + root_path = v; } else if (cutPrefix(u8, arg, "--only=")) |v| { only_n = std.fmt.parseInt(usize, v, 10) catch { print("invalid --only value: {s}\n", .{v}); @@ -197,6 +222,7 @@ pub fn main(init: std.process.Init) !void { .arena = arena, .zig_exe = zig_exe, .work_path = work_path, + .root_path = root_path, }; switch (mode) { @@ -238,7 +264,7 @@ pub fn main(init: std.process.Init) !void { } } else { // All solved. - print("{s}All exercises completed!{s}\n", .{ green_text, reset_text }); + print("{s}All exercises completed!{s}\n", .{ C.green, C.reset }); return; } iterateFrom(ctx, start_index) catch std.process.exit(1); @@ -260,7 +286,7 @@ fn runOne(ctx: Context, ex: Exercise, mode: Mode) Error!void { if (ex.skip) { print("Skipping {s}", .{ex.main_file}); if (ex.skip_hint) |hint| - print("\n{s}Reason: {s}{s}\n", .{ bold_text, hint, reset_text }); + print("\n{s}Reason: {s}{s}\n", .{ C.bold, hint, C.reset }); print("\n\n", .{}); return; } @@ -283,7 +309,7 @@ fn runOne(ctx: Context, ex: Exercise, mode: Mode) Error!void { fn hintAndHelp(ex: Exercise, mode: Mode) void { if (ex.hint) |hint| - print("\n{s}{s}Ziglings hint: {s}{s}", .{ bold_text, green_text, hint, reset_text }); + print("\n{s}{s}Ziglings hint: {s}{s}", .{ C.bold, C.green, hint, C.reset }); help(ex, mode); } @@ -310,7 +336,7 @@ fn runExe(ctx: Context, ex: Exercise) !void { const arena = ctx.arena; print("Compiling {s}...\n", .{ex.main_file}); - const path = std.fs.path.join(arena, &.{ ctx.work_path, ex.main_file }) catch @panic("OOM"); + const path = std.fs.path.join(arena, &.{ ctx.root_path, ctx.work_path, ex.main_file }) catch @panic("OOM"); var argv = std.ArrayList([]const u8).initCapacity(arena, 8) catch @panic("OOM"); argv.append(arena, ctx.zig_exe) catch @panic("OOM"); @@ -328,7 +354,7 @@ fn runExe(ctx: Context, ex: Exercise) !void { .stderr_limit = .limited(1024 * 1024), }) catch |err| { print("{s}error:{s} unable to run {s}: {s}\n", .{ - red_bold_text, reset_text, ex.main_file, @errorName(err), + C.red_bold, C.reset, ex.main_file, @errorName(err), }); return err; }; @@ -344,8 +370,7 @@ fn runTest(ctx: Context, ex: Exercise) !void { const arena = ctx.arena; print("Compiling {s}...\n", .{ex.main_file}); - const path = std.fs.path.join(arena, &.{ ctx.work_path, ex.main_file }) catch - @panic("OOM"); + const path = std.fs.path.join(arena, &.{ ctx.root_path, ctx.work_path, ex.main_file }) catch @panic("OOM"); var argv = std.ArrayList([]const u8).initCapacity(arena, 8) catch @panic("OOM"); argv.append(arena, ctx.zig_exe) catch @panic("OOM"); @@ -362,7 +387,7 @@ fn runTest(ctx: Context, ex: Exercise) !void { .stderr_limit = .limited(1024 * 1024), }) catch |err| { print("{s}error:{s} unable to run test {s}: {s}\n", .{ - red_bold_text, reset_text, ex.main_file, @errorName(err), + C.red_bold, C.reset, ex.main_file, @errorName(err), }); return err; }; @@ -384,7 +409,7 @@ fn checkOutput(io: std.Io, arena: std.mem.Allocator, ex: Exercise, result: Proce }, else => { print("{s}{s} terminated unexpectedly{s}\n", .{ - red_bold_text, ex.main_file, reset_text, + C.red_bold, ex.main_file, C.reset, }); return Error.Failed; }, @@ -419,8 +444,6 @@ fn checkOutput(io: std.Io, arena: std.mem.Allocator, ex: Exercise, result: Proce } if (!std.mem.eql(u8, output, exercise_output)) { - const red = red_bold_text; - const reset = reset_text; print( \\ \\{s}========= expected this output: =========={s} @@ -428,11 +451,11 @@ fn checkOutput(io: std.Io, arena: std.mem.Allocator, ex: Exercise, result: Proce \\{s}========= but found: ====================={s} \\{s} \\{s}=========================================={s} - ++ "\n", .{ red, reset, exercise_output, red, reset, output, red, reset }); + ++ "\n", .{ C.red_bold, C.reset, exercise_output, C.red_bold, C.reset, output, C.red_bold, C.reset }); return Error.Failed; } - print("{s}PASSED:\n{s}{s}\n\n", .{ green_text, output, reset_text }); + print("{s}PASSED:\n{s}{s}\n\n", .{ C.green, output, C.reset }); } fn checkTest(ex: Exercise, result: Process.RunResult) !void { @@ -444,12 +467,12 @@ fn checkTest(ex: Exercise, result: Process.RunResult) !void { }, else => { print("{s}{s} terminated unexpectedly{s}\n", .{ - red_bold_text, ex.main_file, reset_text, + C.red_bold, ex.main_file, C.reset, }); return Error.Failed; }, } - print("{s}PASSED{s}\n\n", .{ green_text, reset_text }); + print("{s}PASSED{s}\n\n", .{ C.green, C.reset }); } fn help(ex: Exercise, mode: Mode) void { @@ -459,12 +482,12 @@ fn help(ex: Exercise, mode: Mode) void { .random => "zig build -Drandom", }; print("\n{s}Edit exercises/{s} and run '{s}' again.{s}\n", .{ - red_bold_text, ex.main_file, cmd, reset_text, + C.red_bold, ex.main_file, cmd, C.reset, }); } fn resetLine() void { - if (use_color_escapes) print("{s}", .{"\x1b[2K\r"}); + if (stderr_term_mode == .escape_codes) print("{s}", .{"\x1b[2K\r"}); } // Removes trailing whitespace per line and any trailing LF at the end.