", .{sgr_num});
}
open_span_count += 1;
},
else => return error.UnsupportedEscape,
},
}
}
return try buf.toOwnedSlice();
}
// Returns true if number is in slice.
fn in(slice: []const u8, number: u8) bool {
return mem.indexOfScalar(u8, slice, number) != null;
}
fn run(
allocator: Allocator,
env_map: *process.EnvMap,
cwd: ?[]const u8,
args: []const []const u8,
) !process.Child.RunResult {
const result = try process.Child.run(.{
.allocator = allocator,
.argv = args,
.env_map = env_map,
.cwd = cwd,
.max_output_bytes = max_doc_file_size,
});
switch (result.term) {
.Exited => |exit_code| {
if (exit_code != 0) {
std.debug.print("{s}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code });
dumpArgs(args);
return error.ChildExitError;
}
},
else => {
std.debug.print("{s}\nThe following command crashed:\n", .{result.stderr});
dumpArgs(args);
return error.ChildCrashed;
},
}
return result;
}
fn printShell(out: anytype, shell_content: []const u8, escape: bool) !void {
const trimmed_shell_content = mem.trim(u8, shell_content, " \r\n");
try out.writeAll("");
}
test "term supported colors" {
const test_allocator = testing.allocator;
{
const input = "A\x1b[31;1mred\x1b[0mB";
const expect = "AredB";
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
{
const input = "A\x1b[32;1mgreen\x1b[0mB";
const expect = "AgreenB";
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
{
const input = "A\x1b[36;1mcyan\x1b[0mB";
const expect = "AcyanB";
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
{
const input = "A\x1b[1mbold\x1b[0mB";
const expect = "AboldB";
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
{
const input = "A\x1b[2mdim\x1b[0mB";
const expect = "AdimB";
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
}
test "term output from zig" {
// Use data generated by https://github.com/perillo/zig-tty-test-data,
// with zig version 0.11.0-dev.1898+36d47dd19.
const test_allocator = testing.allocator;
{
// 1.1-with-build-progress.out
const input = "Semantic Analysis [1324] \x1b[25D\x1b[0KLLVM Emit Object... \x1b[20D\x1b[0KLLVM Emit Object... \x1b[20D\x1b[0KLLD Link... \x1b[12D\x1b[0K";
const expect = "";
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
{
// 2.1-with-reference-traces.out
const input = "\x1b[1msrc/2.1-with-reference-traces.zig:3:7: \x1b[31;1merror: \x1b[0m\x1b[1mcannot assign to constant\n\x1b[0m x += 1;\n \x1b[32;1m~~^~~~\n\x1b[0m\x1b[0m\x1b[2mreferenced by:\n main: src/2.1-with-reference-traces.zig:7:5\n callMain: /usr/local/lib/zig/lib/std/start.zig:607:17\n remaining reference traces hidden; use '-freference-trace' to see all reference traces\n\n\x1b[0m";
const expect =
\\src/2.1-with-reference-traces.zig:3:7: error: cannot assign to constant
\\ x += 1;
\\ ~~^~~~
\\referenced by:
\\ main: src/2.1-with-reference-traces.zig:7:5
\\ callMain: /usr/local/lib/zig/lib/std/start.zig:607:17
\\ remaining reference traces hidden; use '-freference-trace' to see all reference traces
\\
\\
;
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
{
// 2.2-without-reference-traces.out
const input = "\x1b[1m/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:128:29: \x1b[31;1merror: \x1b[0m\x1b[1minvalid type given to fixedBufferStream\n\x1b[0m else => @compileError(\"invalid type given to fixedBufferStream\"),\n \x1b[32;1m^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\x1b[0m\x1b[1m/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:116:66: \x1b[36;1mnote: \x1b[0m\x1b[1mcalled from here\n\x1b[0mpub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) {\n; \x1b[32;1m~~~~~^~~~~~~~~~~~~~~~~\n\x1b[0m";
const expect =
\\/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:128:29: error: invalid type given to fixedBufferStream
\\ else => @compileError("invalid type given to fixedBufferStream"),
\\ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\\/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:116:66: note: called from here
\\pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) {
\\; ~~~~~^~~~~~~~~~~~~~~~~
\\
;
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
{
// 2.3-with-notes.out
const input = "\x1b[1msrc/2.3-with-notes.zig:6:9: \x1b[31;1merror: \x1b[0m\x1b[1mexpected type '*2.3-with-notes.Derp', found '*2.3-with-notes.Wat'\n\x1b[0m bar(w);\n \x1b[32;1m^\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:6:9: \x1b[36;1mnote: \x1b[0m\x1b[1mpointer type child '2.3-with-notes.Wat' cannot cast into pointer type child '2.3-with-notes.Derp'\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:2:13: \x1b[36;1mnote: \x1b[0m\x1b[1mopaque declared here\n\x1b[0mconst Wat = opaque {};\n \x1b[32;1m^~~~~~~~~\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:1:14: \x1b[36;1mnote: \x1b[0m\x1b[1mopaque declared here\n\x1b[0mconst Derp = opaque {};\n \x1b[32;1m^~~~~~~~~\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:4:18: \x1b[36;1mnote: \x1b[0m\x1b[1mparameter type declared here\n\x1b[0mextern fn bar(d: *Derp) void;\n \x1b[32;1m^~~~~\n\x1b[0m\x1b[0m\x1b[2mreferenced by:\n main: src/2.3-with-notes.zig:10:5\n callMain: /usr/local/lib/zig/lib/std/start.zig:607:17\n remaining reference traces hidden; use '-freference-trace' to see all reference traces\n\n\x1b[0m";
const expect =
\\src/2.3-with-notes.zig:6:9: error: expected type '*2.3-with-notes.Derp', found '*2.3-with-notes.Wat'
\\ bar(w);
\\ ^
\\src/2.3-with-notes.zig:6:9: note: pointer type child '2.3-with-notes.Wat' cannot cast into pointer type child '2.3-with-notes.Derp'
\\src/2.3-with-notes.zig:2:13: note: opaque declared here
\\const Wat = opaque {};
\\ ^~~~~~~~~
\\src/2.3-with-notes.zig:1:14: note: opaque declared here
\\const Derp = opaque {};
\\ ^~~~~~~~~
\\src/2.3-with-notes.zig:4:18: note: parameter type declared here
\\extern fn bar(d: *Derp) void;
\\ ^~~~~
\\referenced by:
\\ main: src/2.3-with-notes.zig:10:5
\\ callMain: /usr/local/lib/zig/lib/std/start.zig:607:17
\\ remaining reference traces hidden; use '-freference-trace' to see all reference traces
\\
\\
;
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
{
// 3.1-with-error-return-traces.out
const input = "error: Error\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:5:5\x1b[0m: \x1b[2m0x20b008 in callee (3.1-with-error-return-traces)\x1b[0m\n return error.Error;\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:9:5\x1b[0m: \x1b[2m0x20b113 in caller (3.1-with-error-return-traces)\x1b[0m\n try callee();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:13:5\x1b[0m: \x1b[2m0x20b153 in main (3.1-with-error-return-traces)\x1b[0m\n try caller();\n \x1b[32;1m^\x1b[0m\n";
const expect =
\\error: Error
\\/home/zig/src/3.1-with-error-return-traces.zig:5:5: 0x20b008 in callee (3.1-with-error-return-traces)
\\ return error.Error;
\\ ^
\\/home/zig/src/3.1-with-error-return-traces.zig:9:5: 0x20b113 in caller (3.1-with-error-return-traces)
\\ try callee();
\\ ^
\\/home/zig/src/3.1-with-error-return-traces.zig:13:5: 0x20b153 in main (3.1-with-error-return-traces)
\\ try caller();
\\ ^
\\
;
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
{
// 3.2-with-stack-trace.out
const input = "\x1b[1m/usr/local/lib/zig/lib/std/debug.zig:561:19\x1b[0m: \x1b[2m0x22a107 in writeCurrentStackTrace__anon_5898 (3.2-with-stack-trace)\x1b[0m\n while (it.next()) |return_address| {\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/debug.zig:157:80\x1b[0m: \x1b[2m0x20bb23 in dumpCurrentStackTrace (3.2-with-stack-trace)\x1b[0m\n writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(io.getStdErr()), start_addr) catch |err| {\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.2-with-stack-trace.zig:5:36\x1b[0m: \x1b[2m0x20d3b2 in foo (3.2-with-stack-trace)\x1b[0m\n std.debug.dumpCurrentStackTrace(null);\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.2-with-stack-trace.zig:9:8\x1b[0m: \x1b[2m0x20b458 in main (3.2-with-stack-trace)\x1b[0m\n foo();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/start.zig:607:22\x1b[0m: \x1b[2m0x20a965 in posixCallMainAndExit (3.2-with-stack-trace)\x1b[0m\n root.main();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/start.zig:376:5\x1b[0m: \x1b[2m0x20a411 in _start (3.2-with-stack-trace)\x1b[0m\n @call(.never_inline, posixCallMainAndExit, .{});\n \x1b[32;1m^\x1b[0m\n";
const expect =
\\/usr/local/lib/zig/lib/std/debug.zig:561:19: 0x22a107 in writeCurrentStackTrace__anon_5898 (3.2-with-stack-trace)
\\ while (it.next()) |return_address| {
\\ ^
\\/usr/local/lib/zig/lib/std/debug.zig:157:80: 0x20bb23 in dumpCurrentStackTrace (3.2-with-stack-trace)
\\ writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(io.getStdErr()), start_addr) catch |err| {
\\ ^
\\/home/zig/src/3.2-with-stack-trace.zig:5:36: 0x20d3b2 in foo (3.2-with-stack-trace)
\\ std.debug.dumpCurrentStackTrace(null);
\\ ^
\\/home/zig/src/3.2-with-stack-trace.zig:9:8: 0x20b458 in main (3.2-with-stack-trace)
\\ foo();
\\ ^
\\/usr/local/lib/zig/lib/std/start.zig:607:22: 0x20a965 in posixCallMainAndExit (3.2-with-stack-trace)
\\ root.main();
\\ ^
\\/usr/local/lib/zig/lib/std/start.zig:376:5: 0x20a411 in _start (3.2-with-stack-trace)
\\ @call(.never_inline, posixCallMainAndExit, .{});
\\ ^
\\
;
const result = try termColor(test_allocator, input);
defer test_allocator.free(result);
try testing.expectEqualSlices(u8, expect, result);
}
}
test "printShell" {
const test_allocator = std.testing.allocator;
{
const shell_out =
\\$ zig build test.zig
;
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
{
const shell_out =
\\$ zig build test.zig
\\build output
;
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
{
const shell_out = "$ zig build test.zig\r\nbuild output\r\n";
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
{
const shell_out =
\\$ zig build test.zig
\\build output
\\$ ./test
;
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
{
const shell_out =
\\$ zig build test.zig
\\
\\$ ./test
\\output
;
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
{
const shell_out =
\\$ zig build test.zig
\\$ ./test
\\output
;
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
{
const shell_out =
\\$ zig build test.zig \
\\ --build-option
\\build output
\\$ ./test
\\output
;
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
{
// intentional space after "--build-option1 \"
const shell_out =
\\$ zig build test.zig \
\\ --build-option1 \
\\ --build-option2
\\$ ./test
;
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
{
const shell_out =
\\$ zig build test.zig \
\\$ ./test
;
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
{
const shell_out =
\\$ zig build test.zig
\\$ ./test
\\$1
;
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
{
const shell_out =
\\$zig build test.zig
;
const expected =
\\
;
var buffer = std.ArrayList(u8).init(test_allocator);
defer buffer.deinit();
try printShell(buffer.writer(), shell_out, false);
try testing.expectEqualSlices(u8, expected, buffer.items);
}
}