feat: port to zig-0.16.0 and update flow-syntax and flow-themes

This commit is contained in:
CJ van den Berg 2026-04-16 21:40:27 +02:00
parent a62e5f7d5d
commit 720bc9bc5b
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
6 changed files with 153 additions and 138 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/.cache/
/.zig-cache/
/zig-out/
/zig-pkg/

View file

@ -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_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 "" });
}

View file

@ -1 +0,0 @@
0.14.0

View file

@ -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",

View file

@ -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 };
}

View file

@ -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 <name> 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, &params, parsers, .{
var res = clap.parse(clap.Help, &params, 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, &params, .{}) catch {};
diag.report(stderr, err) catch {};
clap.help(stderr, clap.Help, &params, .{}) 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, &params, .{});
return clap.help(stderr, clap.Help, &params, .{});
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("<div style=\"color:");
@ -438,11 +451,11 @@ fn write_html_preamble(writer: Writer, style: Theme.Style) !void {
try writer.writeAll(";\"><pre>");
}
fn write_html_postamble(writer: Writer) !void {
fn write_html_postamble(writer: *Writer) !void {
try writer.writeAll("</pre></div>");
}
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("<span style=\"color:");
try write_hex_color(writer, color);
@ -457,7 +470,7 @@ fn set_html_style(writer: Writer, style: Theme.Style) !void {
try writer.writeAll(";\">");
}
fn unset_html_style(writer: Writer, _: Theme.Style) !void {
fn unset_html_style(writer: *Writer, _: Theme.Style) !void {
try writer.writeAll("</span>");
}
@ -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);
}