diff --git a/.gitignore b/.gitignore index 4f5f35d..b8d287c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.cache/ /.zig-cache/ /zig-out/ +/zig-pkg/ diff --git a/build.zig b/build.zig index e3b6d80..82b4982 100644 --- a/build.zig +++ b/build.zig @@ -51,11 +51,11 @@ fn build_release( }; const optimize = .ReleaseFast; - var version = std.ArrayList(u8).init(b.allocator); + var version: std.Io.Writer.Allocating = .init(b.allocator); defer version.deinit(); - gen_version(b, version.writer()) catch unreachable; + gen_version(b, &version.writer) catch unreachable; const write_file_step = b.addWriteFiles(); - const version_file = write_file_step.add("version", version.items); + const version_file = write_file_step.add("version", version.written()); b.getInstallStep().dependOn(&b.addInstallFile(version_file, "version").step); for (targets) |t| { @@ -97,10 +97,12 @@ pub fn build_exe( const exe = b.addExecutable(.{ .name = "zat", - .root_source_file = b.path("src/main.zig"), - .target = target, - .optimize = optimize, - .strip = strip, + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + .strip = strip, + }), }); if (pie) |value| exe.pie = value; exe.root_module.addImport("syntax", syntax_dep.module("syntax")); @@ -118,13 +120,13 @@ pub fn build_exe( run_step.dependOn(&run_cmd.step); } -fn gen_version(b: *std.Build, writer: anytype) !void { +fn gen_version(b: *std.Build, writer: *std.Io.Writer) !void { var code: u8 = 0; - const describe = try b.runAllowFail(&[_][]const u8{ "git", "describe", "--always", "--tags" }, &code, .Ignore); - const diff_ = try b.runAllowFail(&[_][]const u8{ "git", "diff", "--stat", "--patch", "HEAD" }, &code, .Ignore); - const diff = std.mem.trimRight(u8, diff_, "\r\n "); - const version = std.mem.trimRight(u8, describe, "\r\n "); + const describe = try b.runAllowFail(&[_][]const u8{ "git", "describe", "--always", "--tags" }, &code, .ignore); + const diff_ = try b.runAllowFail(&[_][]const u8{ "git", "diff", "--stat", "--patch", "HEAD" }, &code, .ignore); + const diff = std.mem.trimEnd(u8, diff_, "\r\n "); + const version = std.mem.trimEnd(u8, describe, "\r\n "); try writer.print("{s}{s}", .{ version, if (diff.len > 0) "-dirty" else "" }); } diff --git a/build.zig.version b/build.zig.version deleted file mode 100644 index a803cc2..0000000 --- a/build.zig.version +++ /dev/null @@ -1 +0,0 @@ -0.14.0 diff --git a/build.zig.zon b/build.zig.zon index f01a828..4845f24 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,20 +1,20 @@ .{ .name = .zat, .version = "1.0.0", - .minimum_zig_version = "0.15.2", + .minimum_zig_version = "0.16.0", .fingerprint = 0x8da9db57fa011a09, .dependencies = .{ .clap = .{ - .url = "https://github.com/Hejsil/zig-clap/archive/0.10.0.tar.gz", - .hash = "clap-0.10.0-oBajB434AQBDh-Ei3YtoKIRxZacVPF1iSwp3IX_ZB8f0", + .url = "git+https://github.com/Hejsil/zig-clap?ref=master#fc1e5cc3f6d9d3001112385ee6256d694e959d2f", + .hash = "clap-0.11.0-oBajB7foAQC3Iyn4IVCkUdYaOVVng5IZkSncySTjNig1", }, .themes = .{ - .url = "https://github.com/neurocyte/flow-themes/releases/download/master-3d26d97bed7e603f3c3846cf5328e3e845df727c/flow-themes.tar.gz", - .hash = "N-V-__8AAK88IwAuRxgh-x0ikK2KRhXCvCg8joy5mLlH0Ffk", + .url = "https://github.com/neurocyte/flow-themes/releases/download/master-9114e2d9ff4e01064ba1da4f780b0c5448b0f0ef/flow-themes.tar.gz", + .hash = "N-V-__8AAJZfLAAP6zSuW13nEIrBBHZM9pbeMRwhvQ-8qqx1", }, .syntax = .{ - .url = "git+https://github.com/neurocyte/flow-syntax?ref=master#02c26ba48fbc962058097feb53dd15f2750b6dc9", - .hash = "flow_syntax-0.7.2-X8jOobBCAQArXM2P0pqy2RXieTrYu2RbCo7FOO8NWvfv", + .url = "git+https://github.com/neurocyte/flow-syntax?ref=master#fd10951562892a8f0f507140ada1dda201445cd6", + .hash = "flow_syntax-0.7.2-X8jOoZJcAQDG_jcA_FMqGZ98xjrrq7W-GscGocAKzfsM", }, .ansi_term = .{ .url = "https://github.com/ziglibs/ansi-term/archive/c0e6ad093d4f6a9ed4e65d962d1e53b97888f989.tar.gz", diff --git a/src/config_loader.zig b/src/config_loader.zig index cb47ede..fec1783 100644 --- a/src/config_loader.zig +++ b/src/config_loader.zig @@ -6,15 +6,15 @@ const builtin = @import("builtin"); const application_name = "flow"; -pub fn read_config(T: type, allocator: std.mem.Allocator) struct { T, [][]const u8 } { +pub fn read_config(io: std.Io, env: *const std.process.Environ.Map, T: type, allocator: std.mem.Allocator) struct { T, [][]const u8 } { var bufs: [][]const u8 = &[_][]const u8{}; - const json_file_name = get_app_config_file_name(application_name, @typeName(T)) catch return .{ .{}, bufs }; + const json_file_name = get_app_config_file_name(io, env, application_name, @typeName(T)) catch return .{ .{}, bufs }; const text_file_name = json_file_name[0 .. json_file_name.len - ".json".len]; var conf: T = .{}; - if (!read_config_file(T, allocator, &conf, &bufs, text_file_name)) { - _ = read_config_file(T, allocator, &conf, &bufs, json_file_name); + if (!read_config_file(io, T, allocator, &conf, &bufs, text_file_name)) { + _ = read_config_file(io, T, allocator, &conf, &bufs, json_file_name); } - read_nested_include_files(T, allocator, &conf, &bufs); + read_nested_include_files(io, T, allocator, &conf, &bufs); return .{ conf, bufs }; } @@ -23,11 +23,11 @@ pub fn free_config(allocator: std.mem.Allocator, bufs: [][]const u8) void { } // returns true if the file was found -fn read_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]const u8, file_name: []const u8) bool { +fn read_config_file(io: std.Io, T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]const u8, file_name: []const u8) bool { std.log.info("loading {s}", .{file_name}); const err: anyerror = blk: { - if (std.mem.endsWith(u8, file_name, ".json")) if (read_json_config_file(T, allocator, conf, bufs, file_name)) return true else |e| break :blk e; - if (read_text_config_file(T, allocator, conf, bufs, file_name)) return true else |e| break :blk e; + if (std.mem.endsWith(u8, file_name, ".json")) if (read_json_config_file(io, T, allocator, conf, bufs, file_name)) return true else |e| break :blk e; + if (read_text_config_file(io, T, allocator, conf, bufs, file_name)) return true else |e| break :blk e; }; switch (err) { error.FileNotFound => return false, @@ -36,33 +36,39 @@ fn read_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][] return true; } -fn read_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void { - var file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }); - defer file.close(); - const text = try file.readToEndAlloc(allocator, 64 * 1024); - defer allocator.free(text); - var cbor_buf = std.ArrayList(u8).init(allocator); +fn read_text_config_file(io: std.Io, T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void { + var file = try std.Io.Dir.openFileAbsolute(io, file_name, .{ .mode = .read_only }); + defer file.close(io); + var read_buf: [4096]u8 = undefined; + var rdr = std.Io.File.reader(file, io, &read_buf); + const content = try rdr.interface.allocRemaining(allocator, .limited(64 * 1024)); + defer allocator.free(content); + return parse_text_config_file(T, allocator, conf, bufs_, file_name, content); +} + +pub fn parse_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8, content: []const u8) !void { + var cbor_buf: std.Io.Writer.Allocating = .init(allocator); defer cbor_buf.deinit(); - const writer = cbor_buf.writer(); - var it = std.mem.splitScalar(u8, text, '\n'); + const writer = &cbor_buf.writer; + var it = std.mem.splitScalar(u8, content, '\n'); var lineno: u32 = 0; while (it.next()) |line| { lineno += 1; if (line.len == 0 or line[0] == '#') continue; - const sep = std.mem.indexOfScalar(u8, line, ' ') orelse { + const spc = std.mem.indexOfScalar(u8, line, ' ') orelse { std.log.err("{s}:{}: {s} missing value", .{ file_name, lineno, line }); continue; }; - const name = line[0..sep]; - const value_str = line[sep + 1 ..]; + const name = line[0..spc]; + const value_str = line[spc + 1 ..]; const cb = cbor.fromJsonAlloc(allocator, value_str) catch { std.log.err("{s}:{}: {s} has bad value: {s}", .{ file_name, lineno, name, value_str }); continue; }; defer allocator.free(cb); try cbor.writeValue(writer, name); - try cbor_buf.appendSlice(cb); + try writer.writeAll(cb); } const cb = try cbor_buf.toOwnedSlice(); var bufs = std.ArrayListUnmanaged([]const u8).fromOwnedSlice(bufs_.*); @@ -71,10 +77,12 @@ fn read_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: return read_cbor_config(T, conf, file_name, cb); } -fn read_json_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void { - var file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }); - defer file.close(); - const json = try file.readToEndAlloc(allocator, 64 * 1024); +fn read_json_config_file(io: std.Io, T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void { + var file = try std.Io.Dir.openFileAbsolute(io, file_name, .{ .mode = .read_only }); + defer file.close(io); + var read_buf: [4096]u8 = undefined; + var rdr = std.Io.File.reader(file, io, &read_buf); + const json = try rdr.interface.allocRemaining(allocator, .limited(64 * 1024)); defer allocator.free(json); const cbor_buf: []u8 = try allocator.alloc(u8, json.len); var bufs = std.ArrayListUnmanaged([]const u8).fromOwnedSlice(bufs_.*); @@ -132,16 +140,16 @@ fn read_cbor_config( } } -fn read_nested_include_files(T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]const u8) void { +fn read_nested_include_files(io: std.Io, T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]const u8) void { if (conf.include_files.len == 0) return; var it = std.mem.splitScalar(u8, conf.include_files, std.fs.path.delimiter); - while (it.next()) |path| if (!read_config_file(T, allocator, conf, bufs, path)) { + while (it.next()) |path| if (!read_config_file(io, T, allocator, conf, bufs, path)) { std.log.warn("config include file '{s}' is not found", .{path}); }; } -pub fn get_config_dir() ![]const u8 { - return get_app_config_dir(application_name); +pub fn get_config_dir(io: std.Io, env: *const std.process.Environ.Map) ![]const u8 { + return get_app_config_dir(io, env, application_name); } pub const ConfigDirError = error{ @@ -152,30 +160,26 @@ pub const ConfigDirError = error{ AppConfigDirUnavailable, }; -fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 { - const a = std.heap.c_allocator; +fn get_app_config_dir(io: std.Io, env: *const std.process.Environ.Map, appname: []const u8) ConfigDirError![]const u8 { const local = struct { var config_dir_buffer: [std.posix.PATH_MAX]u8 = undefined; var config_dir: ?[]const u8 = null; }; const config_dir = if (local.config_dir) |dir| dir - else if (std.process.getEnvVarOwned(a, "XDG_CONFIG_HOME") catch null) |xdg| ret: { - defer a.free(xdg); + else if (env.get("XDG_CONFIG_HOME")) |xdg| ret: { break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/{s}", .{ xdg, appname }); - } else if (std.process.getEnvVarOwned(a, "HOME") catch null) |home| ret: { - defer a.free(home); + } else if (env.get("HOME")) |home| ret: { const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config", .{home}); - std.fs.makeDirAbsolute(dir) catch |e| switch (e) { + std.Io.Dir.createDirAbsolute(io, dir, .default_dir) catch |e| switch (e) { error.PathAlreadyExists => {}, else => return error.MakeHomeConfigDirFailed, }; break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config/{s}", .{ home, appname }); } else if (builtin.os.tag == .windows) ret: { - if (std.process.getEnvVarOwned(a, "APPDATA") catch null) |appdata| { - defer a.free(appdata); + if (env.get("APPDATA")) |appdata| { const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/{s}", .{ appdata, appname }); - std.fs.makeDirAbsolute(dir) catch |e| switch (e) { + std.Io.Dir.createDirAbsolute(io, dir, .default_dir) catch |e| switch (e) { error.PathAlreadyExists => {}, else => return error.MakeAppConfigDirFailed, }; @@ -184,73 +188,73 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 { } else return error.AppConfigDirUnavailable; local.config_dir = config_dir; - std.fs.makeDirAbsolute(config_dir) catch |e| switch (e) { + std.Io.Dir.createDirAbsolute(io, config_dir, .default_dir) catch |e| switch (e) { error.PathAlreadyExists => {}, else => return error.MakeConfigDirFailed, }; var theme_dir_buffer: [std.posix.PATH_MAX]u8 = undefined; - std.fs.makeDirAbsolute(try std.fmt.bufPrint(&theme_dir_buffer, "{s}/{s}", .{ config_dir, theme_dir })) catch {}; + std.Io.Dir.createDirAbsolute(io, try std.fmt.bufPrint(&theme_dir_buffer, "{s}/{s}", .{ config_dir, theme_dir }), .default_dir) catch {}; return config_dir; } -fn get_app_config_file_name(appname: []const u8, comptime base_name: []const u8) ConfigDirError![]const u8 { - return get_app_config_dir_file_name(appname, base_name ++ ".json"); +fn get_app_config_file_name(io: std.Io, env: *const std.process.Environ.Map, appname: []const u8, comptime base_name: []const u8) ConfigDirError![]const u8 { + return get_app_config_dir_file_name(io, env, appname, base_name ++ ".json"); } -fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name: []const u8) ConfigDirError![]const u8 { +fn get_app_config_dir_file_name(io: std.Io, env: *const std.process.Environ.Map, appname: []const u8, comptime config_file_name: []const u8) ConfigDirError![]const u8 { const local = struct { var config_file_buffer: [std.posix.PATH_MAX]u8 = undefined; }; - return std.fmt.bufPrint(&local.config_file_buffer, "{s}/{s}", .{ try get_app_config_dir(appname), config_file_name }); + return std.fmt.bufPrint(&local.config_file_buffer, "{s}/{s}", .{ try get_app_config_dir(io, env, appname), config_file_name }); } const theme_dir = "themes"; -fn get_theme_directory() ![]const u8 { +fn get_theme_directory(io: std.Io, env: *const std.process.Environ.Map) ![]const u8 { const local = struct { var dir_buffer: [std.posix.PATH_MAX]u8 = undefined; }; - const a = std.heap.c_allocator; - if (std.process.getEnvVarOwned(a, "FLOW_THEMES_DIR") catch null) |dir| { - defer a.free(dir); + if (env.get("FLOW_THEMES_DIR")) |dir| { return try std.fmt.bufPrint(&local.dir_buffer, "{s}", .{dir}); } - return try std.fmt.bufPrint(&local.dir_buffer, "{s}/{s}", .{ try get_app_config_dir(application_name), theme_dir }); + return try std.fmt.bufPrint(&local.dir_buffer, "{s}/{s}", .{ try get_app_config_dir(io, env, application_name), theme_dir }); } -pub fn get_theme_file_name(theme_name: []const u8) ![]const u8 { - const dir = try get_theme_directory(); +pub fn get_theme_file_name(io: std.Io, env: *const std.process.Environ.Map, theme_name: []const u8) ![]const u8 { + const dir = try get_theme_directory(io, env); const local = struct { var file_buffer: [std.posix.PATH_MAX]u8 = undefined; }; return try std.fmt.bufPrint(&local.file_buffer, "{s}/{s}.json", .{ dir, theme_name }); } -fn read_theme(allocator: std.mem.Allocator, theme_name: []const u8) ?[]const u8 { - const file_name = get_theme_file_name(theme_name) catch return null; - var file = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch return null; - defer file.close(); - return file.readToEndAlloc(allocator, 64 * 1024) catch null; +fn read_theme(io: std.Io, env: *const std.process.Environ.Map, allocator: std.mem.Allocator, theme_name: []const u8) ?[]const u8 { + const file_name = get_theme_file_name(io, env, theme_name) catch return null; + var file = std.Io.Dir.openFileAbsolute(io, file_name, .{ .mode = .read_only }) catch return null; + defer file.close(io); + var read_buf: [4096]u8 = undefined; + var rdr = std.Io.File.reader(file, io, &read_buf); + return rdr.interface.allocRemaining(allocator, .limited(64 * 1024)) catch null; } -fn load_theme_file(allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Theme) { - return load_theme_file_internal(allocator, theme_name) catch |e| { +fn load_theme_file(io: std.Io, env: *const std.process.Environ.Map, allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Theme) { + return load_theme_file_internal(io, env, allocator, theme_name) catch |e| { std.log.err("loaded theme from file failed: {}", .{e}); return e; }; } -fn load_theme_file_internal(allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Theme) { +fn load_theme_file_internal(io: std.Io, env: *const std.process.Environ.Map, allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Theme) { _ = std.json.Scanner; - const json_str = read_theme(allocator, theme_name) orelse return null; + const json_str = read_theme(io, env, allocator, theme_name) orelse return null; defer allocator.free(json_str); return try std.json.parseFromSlice(Theme, allocator, json_str, .{ .allocate = .alloc_always }); } -pub fn get_theme_by_name(allocator: std.mem.Allocator, name: []const u8) ?struct { Theme, ?std.json.Parsed(Theme) } { - if (load_theme_file(allocator, name) catch null) |parsed_theme| { +pub fn get_theme_by_name(io: std.Io, env: *const std.process.Environ.Map, allocator: std.mem.Allocator, name: []const u8) ?struct { Theme, ?std.json.Parsed(Theme) } { + if (load_theme_file(io, env, allocator, name) catch null) |parsed_theme| { std.log.info("loaded theme from file: {s}", .{name}); return .{ parsed_theme.value, parsed_theme }; } diff --git a/src/main.zig b/src/main.zig index 5f2d6d4..d216abb 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,7 +6,7 @@ const themes = @import("themes"); const term = @import("ansi_term"); const config_loader = @import("config_loader.zig"); -const Writer = std.io.BufferedWriter(4096, std.fs.File.Writer).Writer; +const Writer = std.Io.Writer; const StyleCache = std.AutoHashMap(u32, ?Theme.Token); var style_cache: StyleCache = undefined; var lang_override: ?[]const u8 = null; @@ -18,7 +18,7 @@ pub const std_options: std.Options = .{ .log_level = if (builtin.mode == .Debug) .info else .err, }; -pub fn main() !void { +pub fn main(init: std.process.Init) !void { const params = comptime clap.parseParamsComptime( \\-h, --help Display this help and exit. \\-l, --language Override the language. @@ -38,10 +38,14 @@ pub fn main() !void { \\ ); - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{}; - const a = gpa.allocator(); + const a = init.gpa; style_cache = StyleCache.init(a); + var stderr_buf: [4096]u8 = undefined; + var stderr_file = std.Io.File.writer(std.Io.File.stderr(), init.io, &stderr_buf); + const stderr = &stderr_file.interface; + defer stderr_file.flush() catch {}; + const parsers = comptime .{ .name = clap.parsers.string, .file = clap.parsers.string, @@ -49,36 +53,38 @@ pub fn main() !void { .lines = clap.parsers.int(usize, 10), }; var diag = clap.Diagnostic{}; - var res = clap.parse(clap.Help, ¶ms, parsers, .{ + var res = clap.parse(clap.Help, ¶ms, parsers, init.minimal.args, .{ .diagnostic = &diag, .allocator = a, }) catch |err| { - diag.report(std.io.getStdErr().writer(), err) catch {}; - clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}) catch {}; + diag.report(stderr, err) catch {}; + clap.help(stderr, clap.Help, ¶ms, .{}) catch {}; std.process.exit(1); return err; }; defer res.deinit(); - const stdout_file = std.io.getStdOut(); - const stdout_writer = stdout_file.writer(); - var bw = std.io.bufferedWriter(stdout_writer); - const writer = bw.writer(); - defer bw.flush() catch {}; + var stdout_buf: [4096]u8 = undefined; + var stdout_file = std.Io.File.writer(std.Io.File.stdout(), init.io, &stdout_buf); + const stdout = &stdout_file.interface; + defer stdout_file.flush() catch {}; if (res.args.help != 0) - return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); + return clap.help(stderr, clap.Help, ¶ms, .{}); if (res.args.@"list-themes" != 0) - return list_themes(writer); + return list_themes(stdout); if (res.args.@"list-languages" != 0) - return list_langs(writer); + return list_langs(stdout); - if (res.args.color == 0 and !stdout_file.supportsAnsiEscapeCodes()) - return plain_cat(res.positionals[0]); + if (res.args.color == 0) { + const tty_mode = std.Io.Terminal.Mode.detect(init.io, std.Io.File.stdout(), false, false) catch .no_color; + if (tty_mode == .no_color) + return plain_cat(init.io, stdout, res.positionals[0]); + } - const conf, const conf_bufs = config_loader.read_config(@import("config.zig"), a); + const conf, const conf_bufs = config_loader.read_config(init.io, init.environ_map, @import("config.zig"), a); defer config_loader.free_config(a, conf_bufs); const theme_name = if (res.args.theme) |theme| theme else conf.theme; const limit_lines = res.args.limit; @@ -98,7 +104,7 @@ pub fn main() !void { std.process.exit(1); } - const theme, const parsed_theme = config_loader.get_theme_by_name(a, theme_name) orelse { + const theme, const parsed_theme = config_loader.get_theme_by_name(init.io, init.environ_map, a, theme_name) orelse { std.log.err("theme \"{s}\" not found", .{theme_name}); std.process.exit(1); }; @@ -111,20 +117,23 @@ pub fn main() !void { if (res.args.default) |default| lang_default = default; if (res.args.html != 0) - try write_html_preamble(writer, theme.editor); + try write_html_preamble(stdout, theme.editor); if (res.positionals[0].len > 0) { for (res.positionals[0]) |arg| { const file = if (std.mem.eql(u8, arg, "-")) - std.io.getStdIn() + std.Io.File.stdin() else - try std.fs.cwd().openFile(arg, .{ .mode = .read_only }); - defer file.close(); - const content = try file.readToEndAlloc(a, std.math.maxInt(u32)); + try std.Io.Dir.cwd().openFile(init.io, arg, .{ .mode = .read_only }); + defer file.close(init.io); + var file_read_buf: [4096]u8 = undefined; + var file_rdr = std.Io.File.reader(file, init.io, &file_read_buf); + const content = try file_rdr.interface.allocRemaining(a, .limited(std.math.maxInt(u32))); defer a.free(content); render_file( + init.io, a, - writer, + stdout, content, arg, &theme, @@ -139,14 +148,16 @@ pub fn main() !void { error.Stop => return, else => return e, }; - try bw.flush(); } } else { - const content = try std.io.getStdIn().readToEndAlloc(a, std.math.maxInt(u32)); + var stdin_read_buf: [4096]u8 = undefined; + var stdin_rdr = std.Io.File.reader(std.Io.File.stdin(), init.io, &stdin_read_buf); + const content = try stdin_rdr.interface.allocRemaining(a, .limited(std.math.maxInt(u32))); defer a.free(content); render_file( + init.io, a, - writer, + stdout, content, "-", &theme, @@ -164,7 +175,7 @@ pub fn main() !void { } if (res.args.html != 0) - try write_html_postamble(writer); + try write_html_postamble(stdout); } fn get_parser(a: std.mem.Allocator, content: []const u8, file_path: []const u8, query_cache: *syntax.QueryCache) struct { syntax.FileType, *syntax } { @@ -184,11 +195,12 @@ fn unknown_file_type(name: []const u8) noreturn { std.process.exit(1); } -const StyleFn = *const fn (writer: Writer, style: Theme.Style) Writer.Error!void; +const StyleFn = *const fn (writer: *Writer, style: Theme.Style) Writer.Error!void; fn render_file( + io: std.Io, a: std.mem.Allocator, - writer: Writer, + writer: *Writer, content: []const u8, file_path: []const u8, theme: *const Theme, @@ -215,7 +227,7 @@ fn render_file( end_line = start_line + lines; } - const query_cache = try syntax.QueryCache.create(a, .{}); + const query_cache = try syntax.QueryCache.create(io, a, .{}); const file_type, const parser = get_parser(a, content, file_path, query_cache); try parser.refresh_full(content); if (show_file_type) { @@ -397,20 +409,21 @@ pub const fallbacks: []const FallBack = &[_]FallBack{ .{ .ts = "field", .tm = "variable" }, }; -fn list_themes(writer: Writer) !void { +fn list_themes(writer: *Writer) !void { var max_name_len: usize = 0; for (themes.themes) |theme| max_name_len = @max(max_name_len, theme.name.len); for (themes.themes) |theme| { try writer.writeAll(theme.name); - try writer.writeByteNTimes(' ', max_name_len + 2 - theme.name.len); + for (0..max_name_len + 2 - theme.name.len) |_| + try writer.writeByte(' '); try writer.writeAll(theme.description); try writer.writeAll("\n"); } } -fn set_ansi_style(writer: Writer, style: Theme.Style) Writer.Error!void { +fn set_ansi_style(writer: *Writer, style: Theme.Style) Writer.Error!void { const ansi_style: term.style.Style = .{ .foreground = if (style.fg) |color| to_rgb_color(color.color) else .Default, .background = if (style.bg) |color| to_rgb_color(color.color) else .Default, @@ -428,7 +441,7 @@ fn set_ansi_style(writer: Writer, style: Theme.Style) Writer.Error!void { const unset_ansi_style = set_ansi_style; -fn write_html_preamble(writer: Writer, style: Theme.Style) !void { +fn write_html_preamble(writer: *Writer, style: Theme.Style) !void { const color = if (style.fg) |color| color.color else 0; const background = if (style.bg) |background| background.color else 0xFFFFFF; try writer.writeAll("
");
 }
 
-fn write_html_postamble(writer: Writer) !void {
+fn write_html_postamble(writer: *Writer) !void {
     try writer.writeAll("
"); } -fn set_html_style(writer: Writer, style: Theme.Style) !void { +fn set_html_style(writer: *Writer, style: Theme.Style) !void { const color = if (style.fg) |color| color.color else 0; try writer.writeAll(""); } -fn unset_html_style(writer: Writer, _: Theme.Style) !void { +fn unset_html_style(writer: *Writer, _: Theme.Style) !void { try writer.writeAll(""); } @@ -468,18 +481,18 @@ fn to_rgb_color(color: u24) term.style.Color { return .{ .RGB = .{ .r = r, .g = g, .b = b } }; } -fn write_hex_color(writer: Writer, color: u24) !void { +fn write_hex_color(writer: *Writer, color: u24) !void { try writer.print("#{x:0>6}", .{color}); } -fn list_langs(writer: Writer) !void { +fn list_langs(writer: *Writer) !void { for (syntax.FileType.get_all()) |file_type| { try writer.writeAll(file_type.name); try writer.writeAll("\n"); } } -fn render_file_type(writer: Writer, file_type: *const syntax.FileType, theme: *const Theme) !void { +fn render_file_type(writer: *Writer, file_type: *const syntax.FileType, theme: *const Theme) !void { const style = theme.editor_selection; const reversed = Theme.Style{ .fg = theme.editor_selection.bg }; const plain: Theme.Style = Theme.Style{ .fg = theme.editor.fg }; @@ -499,7 +512,7 @@ fn render_file_type(writer: Writer, file_type: *const syntax.FileType, theme: *c try writer.writeAll("\n"); } -fn render_theme_indicator(writer: Writer, theme: *const Theme) !void { +fn render_theme_indicator(writer: *Writer, theme: *const Theme) !void { const style = Theme.Style{ .bg = theme.editor_selection.bg, .fg = theme.editor.fg }; const reversed = Theme.Style{ .fg = theme.editor_selection.bg }; const plain: Theme.Style = Theme.Style{ .fg = theme.editor.fg }; @@ -513,26 +526,22 @@ fn render_theme_indicator(writer: Writer, theme: *const Theme) !void { try writer.writeAll("\n"); } -fn plain_cat(files: []const []const u8) !void { - const stdout = std.io.getStdOut(); +fn plain_cat(io: std.Io, stdout: *Writer, files: []const []const u8) !void { if (files.len == 0) { - try plain_cat_file(stdout, "-"); + try plain_cat_file(io, stdout, "-"); } else { - for (files) |file| try plain_cat_file(stdout, file); + for (files) |file| try plain_cat_file(io, stdout, file); } } -fn plain_cat_file(out_file: std.fs.File, in_file_name: []const u8) !void { +fn plain_cat_file(io: std.Io, stdout: *Writer, in_file_name: []const u8) !void { var in_file = if (std.mem.eql(u8, in_file_name, "-")) - std.io.getStdIn() + std.Io.File.stdin() else - try std.fs.cwd().openFile(in_file_name, .{}); - defer in_file.close(); + try std.Io.Dir.cwd().openFile(io, in_file_name, .{}); + defer in_file.close(io); var buf: [std.heap.page_size_min]u8 = undefined; - while (true) { - const bytes_read = try in_file.read(&buf); - if (bytes_read == 0) return; - try out_file.writeAll(buf[0..bytes_read]); - } + var rdr = std.Io.File.reader(in_file, io, &buf); + _ = try rdr.interface.streamRemaining(stdout); }