Compare commits
No commits in common. "master" and "v1.0.0" have entirely different histories.
7 changed files with 95 additions and 279 deletions
12
build.zig
12
build.zig
|
@ -86,14 +86,12 @@ pub fn build_exe(
|
|||
strip: bool,
|
||||
pie: ?bool,
|
||||
) void {
|
||||
|
||||
const clap_dep = b.dependency("clap", .{ .target = target, .optimize = optimize });
|
||||
const ansi_term_dep = b.dependency("ansi_term", .{ .target = target, .optimize = optimize });
|
||||
const ansi_term_dep = b.dependency("ansi-term", .{ .target = target, .optimize = optimize });
|
||||
const themes_dep = b.dependency("themes", .{});
|
||||
const syntax_dep = b.dependency("syntax", .{ .target = target, .optimize = optimize });
|
||||
const cbor_dep = syntax_dep.builder.dependency("cbor", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
const thespian_dep = b.dependency("thespian", .{});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "zat",
|
||||
|
@ -107,8 +105,8 @@ pub fn build_exe(
|
|||
exe.root_module.addImport("theme", themes_dep.module("theme"));
|
||||
exe.root_module.addImport("themes", themes_dep.module("themes"));
|
||||
exe.root_module.addImport("clap", clap_dep.module("clap"));
|
||||
exe.root_module.addImport("ansi_term", ansi_term_dep.module("ansi_term"));
|
||||
exe.root_module.addImport("cbor", cbor_dep.module("cbor"));
|
||||
exe.root_module.addImport("ansi-term", ansi_term_dep.module("ansi-term"));
|
||||
exe.root_module.addImport("cbor", b.createModule(.{ .root_source_file = thespian_dep.path("src/cbor.zig") }));
|
||||
const exe_install = b.addInstallArtifact(exe, exe_install_options);
|
||||
b.getInstallStep().dependOn(&exe_install.step);
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.14.0
|
||||
0.13.0
|
||||
|
|
|
@ -1,23 +1,26 @@
|
|||
.{
|
||||
.name = .zat,
|
||||
.name = "zat",
|
||||
.version = "1.0.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 = "https://github.com/Hejsil/zig-clap/archive/c0193e9247335a6c1688b946325060289405de2a.tar.gz",
|
||||
.hash = "12207ee987ce045596cb992cfb15b0d6d9456e50d4721c3061c69dabc2962053644d",
|
||||
},
|
||||
.themes = .{
|
||||
.url = "https://github.com/neurocyte/flow-themes/releases/download/master-ac2e3fe2df3419b71276f86fa9c45fd39d668f23/flow-themes.tar.gz",
|
||||
.hash = "N-V-__8AAEtaFwAjAHCmWHRCrBxL7uSG4hQiIsSgS32Y67K6",
|
||||
.url = "https://github.com/neurocyte/flow-themes/releases/download/master-5f1ca2fd3c784d430306a5cd1df237681a196333/flow-themes.tar.gz",
|
||||
.hash = "122095b1a6110b920571c7e49e61c124cd9a164fe9b1b0faa1bd11d04d89822d3304",
|
||||
},
|
||||
.syntax = .{
|
||||
.url = "https://github.com/neurocyte/flow-syntax/archive/fa6a411bc769882acc87cf0d961af3813abf2eac.tar.gz",
|
||||
.hash = "flow_syntax-0.1.0-X8jOof39AADK25RT1Bst_x7aUIwHbh7y09PJXBghLu_b",
|
||||
.url = "https://github.com/neurocyte/flow-syntax/archive/2345f2f3b1def47bbc9b34f1404cd4bc9a02c05e.tar.gz",
|
||||
.hash = "1220ea8e786a7f7960b4ea7af84e340f630de5ff145dc94aedde00951ae8f58111f4",
|
||||
},
|
||||
.ansi_term = .{
|
||||
.url = "https://github.com/ziglibs/ansi-term/archive/c0e6ad093d4f6a9ed4e65d962d1e53b97888f989.tar.gz",
|
||||
.hash = "ansi_term-0.1.0-_baAywpoAABEqsPmS5Jz_CddDCrG8qdIyRIESH8D2fzd",
|
||||
.thespian = .{
|
||||
.url = "https://github.com/neurocyte/thespian/archive/d7dd27116398b17c8ab68327c384885f161d0cc1.tar.gz",
|
||||
.hash = "1220ace715c2ee9087fe375996b8e9180bfc722b2d1acdcbf00e8b507b31dd1cdd94",
|
||||
},
|
||||
.@"ansi-term" = .{
|
||||
.url = "https://github.com/ziglibs/ansi-term/archive/0bb62115db6749044765fdb37c9791388e7970f2.tar.gz",
|
||||
.hash = "12200719196e0abd325efa248fb03882c8fa2c7130e3ae1d57dbff72afc846b28495",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
|
|
|
@ -15,4 +15,4 @@ rg \
|
|||
--layout=reverse \
|
||||
--ansi \
|
||||
--tiebreak=index \
|
||||
--preview 'zat --color --highlight {2} --limit $FZF_PREVIEW_LINES {1}'
|
||||
--preview 'zat --highlight {2} --limit $FZF_PREVIEW_LINES {1}'
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
theme: []const u8 = "default",
|
||||
include_files: []const u8 = "",
|
||||
|
|
|
@ -1,158 +1,54 @@
|
|||
const std = @import("std");
|
||||
const cbor = @import("cbor");
|
||||
const Theme = @import("theme");
|
||||
const themes = @import("themes");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const application_name = "flow";
|
||||
|
||||
pub fn read_config(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 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);
|
||||
}
|
||||
read_nested_include_files(T, allocator, &conf, &bufs);
|
||||
return .{ conf, bufs };
|
||||
const config = struct {
|
||||
theme: []const u8 = "default",
|
||||
};
|
||||
|
||||
pub fn read_config(a: std.mem.Allocator, buf: *?[]const u8) config {
|
||||
const file_name = get_app_config_file_name(application_name) catch return .{};
|
||||
return read_json_config_file(a, file_name, buf) catch .{};
|
||||
}
|
||||
|
||||
pub fn free_config(allocator: std.mem.Allocator, bufs: [][]const u8) void {
|
||||
for (bufs) |buf| allocator.free(buf);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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;
|
||||
fn read_json_config_file(a: std.mem.Allocator, file_name: []const u8, buf: *?[]const u8) !config {
|
||||
var file = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch |e| switch (e) {
|
||||
error.FileNotFound => return .{},
|
||||
else => return e,
|
||||
};
|
||||
switch (err) {
|
||||
error.FileNotFound => return false,
|
||||
else => |e| std.log.err("error reading config file '{s}': {s}", .{ file_name, @errorName(e) }),
|
||||
}
|
||||
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);
|
||||
defer cbor_buf.deinit();
|
||||
const writer = cbor_buf.writer();
|
||||
var it = std.mem.splitScalar(u8, text, '\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 {
|
||||
std.log.err("{s}:{}: {s} missing value", .{ file_name, lineno, line });
|
||||
continue;
|
||||
};
|
||||
const name = line[0..sep];
|
||||
const value_str = line[sep + 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);
|
||||
}
|
||||
const cb = try cbor_buf.toOwnedSlice();
|
||||
var bufs = std.ArrayListUnmanaged([]const u8).fromOwnedSlice(bufs_.*);
|
||||
bufs.append(allocator, cb) catch @panic("OOM:read_text_config_file");
|
||||
bufs_.* = bufs.toOwnedSlice(allocator) catch @panic("OOM:read_text_config_file");
|
||||
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);
|
||||
defer allocator.free(json);
|
||||
const cbor_buf: []u8 = try allocator.alloc(u8, json.len);
|
||||
var bufs = std.ArrayListUnmanaged([]const u8).fromOwnedSlice(bufs_.*);
|
||||
bufs.append(allocator, cbor_buf) catch @panic("OOM:read_json_config_file");
|
||||
bufs_.* = bufs.toOwnedSlice(allocator) catch @panic("OOM:read_json_config_file");
|
||||
const json = try file.readToEndAlloc(a, 64 * 1024);
|
||||
defer a.free(json);
|
||||
const cbor_buf: []u8 = try a.alloc(u8, json.len);
|
||||
buf.* = cbor_buf;
|
||||
const cb = try cbor.fromJson(json, cbor_buf);
|
||||
var iter = cb;
|
||||
_ = try cbor.decodeMapHeader(&iter);
|
||||
return read_cbor_config(T, conf, file_name, iter);
|
||||
}
|
||||
|
||||
fn read_cbor_config(
|
||||
T: type,
|
||||
conf: *T,
|
||||
file_name: []const u8,
|
||||
cb: []const u8,
|
||||
) !void {
|
||||
var iter = cb;
|
||||
var field_name: []const u8 = undefined;
|
||||
while (cbor.matchString(&iter, &field_name) catch |e| switch (e) {
|
||||
error.TooShort => return,
|
||||
else => return e,
|
||||
}) {
|
||||
var known = false;
|
||||
inline for (@typeInfo(T).@"struct".fields) |field_info|
|
||||
if (comptime std.mem.eql(u8, "include_files", field_info.name)) {
|
||||
if (std.mem.eql(u8, field_name, field_info.name)) {
|
||||
known = true;
|
||||
var value: field_info.type = undefined;
|
||||
if (try cbor.matchValue(&iter, cbor.extract(&value))) {
|
||||
if (conf.include_files.len > 0) {
|
||||
std.log.warn("{s}: ignoring nested 'include_files' value '{s}'", .{ file_name, value });
|
||||
} else {
|
||||
@field(conf, field_info.name) = value;
|
||||
}
|
||||
} else {
|
||||
try cbor.skipValue(&iter);
|
||||
std.log.err("invalid value for key '{s}'", .{field_name});
|
||||
}
|
||||
}
|
||||
} else if (std.mem.eql(u8, field_name, field_info.name)) {
|
||||
known = true;
|
||||
var len = try cbor.decodeMapHeader(&iter);
|
||||
var data: config = .{};
|
||||
while (len > 0) : (len -= 1) {
|
||||
var found = false;
|
||||
var field_name: []const u8 = undefined;
|
||||
if (!(try cbor.matchString(&iter, &field_name))) return error.InvalidConfig;
|
||||
inline for (@typeInfo(config).Struct.fields) |field_info| {
|
||||
if (std.mem.eql(u8, field_name, field_info.name)) {
|
||||
var value: field_info.type = undefined;
|
||||
if (try cbor.matchValue(&iter, cbor.extract(&value))) {
|
||||
@field(conf, field_info.name) = value;
|
||||
} else {
|
||||
try cbor.skipValue(&iter);
|
||||
std.log.err("invalid value for key '{s}'", .{field_name});
|
||||
}
|
||||
};
|
||||
if (!known) {
|
||||
try cbor.skipValue(&iter);
|
||||
std.log.warn("unknown config value '{s}' ignored", .{field_name});
|
||||
if (!(try cbor.matchValue(&iter, cbor.extract(&value)))) return error.InvalidConfig;
|
||||
@field(data, field_info.name) = value;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) try cbor.skipValue(&iter);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_nested_include_files(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)) {
|
||||
std.log.warn("config include file '{s}' is not found", .{path});
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
pub fn get_config_dir() ![]const u8 {
|
||||
return get_app_config_dir(application_name);
|
||||
}
|
||||
|
||||
pub const ConfigDirError = error{
|
||||
NoSpaceLeft,
|
||||
MakeConfigDirFailed,
|
||||
MakeHomeConfigDirFailed,
|
||||
MakeAppConfigDirFailed,
|
||||
AppConfigDirUnavailable,
|
||||
};
|
||||
|
||||
fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
|
||||
fn get_app_config_dir(appname: []const u8) ![]const u8 {
|
||||
const a = std.heap.c_allocator;
|
||||
const local = struct {
|
||||
var config_dir_buffer: [std.posix.PATH_MAX]u8 = undefined;
|
||||
|
@ -168,7 +64,7 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
|
|||
const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config", .{home});
|
||||
std.fs.makeDirAbsolute(dir) catch |e| switch (e) {
|
||||
error.PathAlreadyExists => {},
|
||||
else => return error.MakeHomeConfigDirFailed,
|
||||
else => return e,
|
||||
};
|
||||
break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config/{s}", .{ home, appname });
|
||||
} else if (builtin.os.tag == .windows) ret: {
|
||||
|
@ -177,7 +73,7 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
|
|||
const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/{s}", .{ appdata, appname });
|
||||
std.fs.makeDirAbsolute(dir) catch |e| switch (e) {
|
||||
error.PathAlreadyExists => {},
|
||||
else => return error.MakeAppConfigDirFailed,
|
||||
else => return e,
|
||||
};
|
||||
break :ret dir;
|
||||
} else return error.AppConfigDirUnavailable;
|
||||
|
@ -186,79 +82,21 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
|
|||
local.config_dir = config_dir;
|
||||
std.fs.makeDirAbsolute(config_dir) catch |e| switch (e) {
|
||||
error.PathAlreadyExists => {},
|
||||
else => return error.MakeConfigDirFailed,
|
||||
else => return e,
|
||||
};
|
||||
|
||||
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 {};
|
||||
|
||||
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_dir_file_name(appname: []const u8, comptime config_file_name: []const u8) ConfigDirError![]const u8 {
|
||||
fn get_app_config_file_name(appname: []const u8) ![]const u8 {
|
||||
const local = struct {
|
||||
var config_file_buffer: [std.posix.PATH_MAX]u8 = undefined;
|
||||
var config_file: ?[]const u8 = null;
|
||||
};
|
||||
return std.fmt.bufPrint(&local.config_file_buffer, "{s}/{s}", .{ try get_app_config_dir(appname), config_file_name });
|
||||
}
|
||||
|
||||
const theme_dir = "themes";
|
||||
|
||||
fn get_theme_directory() ![]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);
|
||||
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 });
|
||||
}
|
||||
|
||||
pub fn get_theme_file_name(theme_name: []const u8) ![]const u8 {
|
||||
const dir = try get_theme_directory();
|
||||
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 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| {
|
||||
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) {
|
||||
_ = std.json.Scanner;
|
||||
const json_str = read_theme(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| {
|
||||
std.log.info("loaded theme from file: {s}", .{name});
|
||||
return .{ parsed_theme.value, parsed_theme };
|
||||
}
|
||||
|
||||
std.log.info("loading theme: {s}", .{name});
|
||||
for (themes.themes) |theme_| {
|
||||
if (std.mem.eql(u8, theme_.name, name))
|
||||
return .{ theme_, null };
|
||||
}
|
||||
return null;
|
||||
const config_file_name = "config.json";
|
||||
const config_file = if (local.config_file) |file|
|
||||
file
|
||||
else
|
||||
try std.fmt.bufPrint(&local.config_file_buffer, "{s}/{s}", .{ try get_app_config_dir(appname), config_file_name });
|
||||
local.config_file = config_file;
|
||||
return config_file;
|
||||
}
|
||||
|
|
86
src/main.zig
86
src/main.zig
|
@ -3,7 +3,7 @@ const clap = @import("clap");
|
|||
const syntax = @import("syntax");
|
||||
const Theme = @import("theme");
|
||||
const themes = @import("themes");
|
||||
const term = @import("ansi_term");
|
||||
const term = @import("ansi-term");
|
||||
const config_loader = @import("config_loader.zig");
|
||||
|
||||
const Writer = std.io.BufferedWriter(4096, std.fs.File.Writer).Writer;
|
||||
|
@ -13,11 +13,6 @@ var lang_override: ?[]const u8 = null;
|
|||
var lang_default: []const u8 = "conf";
|
||||
const no_highlight = std.math.maxInt(usize);
|
||||
|
||||
const builtin = @import("builtin");
|
||||
pub const std_options: std.Options = .{
|
||||
.log_level = if (builtin.mode == .Debug) .info else .err,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
const params = comptime clap.parseParamsComptime(
|
||||
\\-h, --help Display this help and exit.
|
||||
|
@ -25,8 +20,6 @@ pub fn main() !void {
|
|||
\\-t, --theme <name> Select theme to use.
|
||||
\\-d, --default <name> Set the language to use if guessing failed (default: conf).
|
||||
\\-s, --show-language Show detected language in output.
|
||||
\\-T, --show-theme Show selected theme in output.
|
||||
\\-C, --color Always produce color output, even if stdout is not a tty.
|
||||
\\--html Output HTML instead of ansi escape codes.
|
||||
\\--list-themes Show available themes.
|
||||
\\--list-languages Show available language parsers.
|
||||
|
@ -75,11 +68,11 @@ pub fn main() !void {
|
|||
if (res.args.@"list-languages" != 0)
|
||||
return list_langs(writer);
|
||||
|
||||
if (res.args.color == 0 and !stdout_file.supportsAnsiEscapeCodes())
|
||||
return plain_cat(res.positionals[0]);
|
||||
if (!stdout_file.supportsAnsiEscapeCodes())
|
||||
return plain_cat(res.positionals);
|
||||
|
||||
const conf, const conf_bufs = config_loader.read_config(@import("config.zig"), a);
|
||||
defer config_loader.free_config(a, conf_bufs);
|
||||
var conf_buf: ?[]const u8 = null;
|
||||
const conf = config_loader.read_config(a, &conf_buf);
|
||||
const theme_name = if (res.args.theme) |theme| theme else conf.theme;
|
||||
const limit_lines = res.args.limit;
|
||||
|
||||
|
@ -94,15 +87,14 @@ pub fn main() !void {
|
|||
}
|
||||
|
||||
if (highlight_line_end < highlight_line_start) {
|
||||
std.log.err("invalid range", .{});
|
||||
try std.io.getStdErr().writer().print("invalid range\n", .{});
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
const theme, const parsed_theme = config_loader.get_theme_by_name(a, theme_name) orelse {
|
||||
std.log.err("theme \"{s}\" not found", .{theme_name});
|
||||
const theme = get_theme_by_name(theme_name) orelse {
|
||||
try std.io.getStdErr().writer().print("theme \"{s}\" not found\n", .{theme_name});
|
||||
std.process.exit(1);
|
||||
};
|
||||
_ = parsed_theme;
|
||||
|
||||
const set_style: StyleFn = if (res.args.html != 0) set_html_style else set_ansi_style;
|
||||
const unset_style: StyleFn = if (res.args.html != 0) unset_html_style else unset_ansi_style;
|
||||
|
@ -113,8 +105,8 @@ pub fn main() !void {
|
|||
if (res.args.html != 0)
|
||||
try write_html_preamble(writer, theme.editor);
|
||||
|
||||
if (res.positionals[0].len > 0) {
|
||||
for (res.positionals[0]) |arg| {
|
||||
if (res.positionals.len > 0) {
|
||||
for (res.positionals) |arg| {
|
||||
const file = if (std.mem.eql(u8, arg, "-"))
|
||||
std.io.getStdIn()
|
||||
else
|
||||
|
@ -129,7 +121,6 @@ pub fn main() !void {
|
|||
arg,
|
||||
&theme,
|
||||
res.args.@"show-language" != 0,
|
||||
res.args.@"show-theme" != 0,
|
||||
set_style,
|
||||
unset_style,
|
||||
highlight_line_start,
|
||||
|
@ -151,7 +142,6 @@ pub fn main() !void {
|
|||
"-",
|
||||
&theme,
|
||||
res.args.@"show-language" != 0,
|
||||
res.args.@"show-theme" != 0,
|
||||
set_style,
|
||||
unset_style,
|
||||
highlight_line_start,
|
||||
|
@ -167,15 +157,15 @@ pub fn main() !void {
|
|||
try write_html_postamble(writer);
|
||||
}
|
||||
|
||||
fn get_parser(a: std.mem.Allocator, content: []const u8, file_path: []const u8, query_cache: *syntax.QueryCache) *syntax {
|
||||
fn get_parser(a: std.mem.Allocator, content: []const u8, file_path: []const u8) *syntax {
|
||||
return (if (lang_override) |name|
|
||||
syntax.create_file_type(a, name, query_cache) catch unknown_file_type(name)
|
||||
syntax.create_file_type(a, name) catch unknown_file_type(name)
|
||||
else
|
||||
syntax.create_guess_file_type(a, content, file_path, query_cache)) catch syntax.create_file_type(a, lang_default, query_cache) catch unknown_file_type(lang_default);
|
||||
syntax.create_guess_file_type(a, content, file_path)) catch syntax.create_file_type(a, lang_default) catch unknown_file_type(lang_default);
|
||||
}
|
||||
|
||||
fn unknown_file_type(name: []const u8) noreturn {
|
||||
std.log.err("unknown file type \'{s}\'\n", .{name});
|
||||
std.io.getStdErr().writer().print("unknown file type \'{s}\'\n", .{name}) catch {};
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
|
@ -187,8 +177,7 @@ fn render_file(
|
|||
content: []const u8,
|
||||
file_path: []const u8,
|
||||
theme: *const Theme,
|
||||
show_file_type: bool,
|
||||
show_theme: bool,
|
||||
show: bool,
|
||||
set_style: StyleFn,
|
||||
unset_style: StyleFn,
|
||||
highlight_line_start: usize,
|
||||
|
@ -210,17 +199,12 @@ fn render_file(
|
|||
end_line = start_line + lines;
|
||||
}
|
||||
|
||||
const query_cache = try syntax.QueryCache.create(a, .{});
|
||||
const parser = get_parser(a, content, file_path, query_cache);
|
||||
const parser = get_parser(a, content, file_path);
|
||||
try parser.refresh_full(content);
|
||||
if (show_file_type) {
|
||||
if (show) {
|
||||
try render_file_type(writer, parser.file_type, theme);
|
||||
end_line -= 1;
|
||||
}
|
||||
if (show_theme) {
|
||||
try render_theme_indicator(writer, theme);
|
||||
end_line -= 1;
|
||||
}
|
||||
|
||||
const Ctx = struct {
|
||||
writer: @TypeOf(writer),
|
||||
|
@ -392,6 +376,14 @@ pub const fallbacks: []const FallBack = &[_]FallBack{
|
|||
.{ .ts = "field", .tm = "variable" },
|
||||
};
|
||||
|
||||
fn get_theme_by_name(name: []const u8) ?Theme {
|
||||
for (themes.themes) |theme| {
|
||||
if (std.mem.eql(u8, theme.name, name))
|
||||
return theme;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn list_themes(writer: Writer) !void {
|
||||
var max_name_len: usize = 0;
|
||||
for (themes.themes) |theme|
|
||||
|
@ -406,16 +398,16 @@ fn list_themes(writer: Writer) !void {
|
|||
}
|
||||
|
||||
fn set_ansi_style(writer: Writer, style: Theme.Style) Writer.Error!void {
|
||||
const ansi_style: term.style.Style = .{
|
||||
const ansi_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,
|
||||
.font_style = switch (style.fs orelse .normal) {
|
||||
.normal => term.style.FontStyle{},
|
||||
.bold => term.style.FontStyle{ .bold = true },
|
||||
.italic => term.style.FontStyle{ .italic = true },
|
||||
.underline => term.style.FontStyle{ .underline = true },
|
||||
.undercurl => term.style.FontStyle{ .underline = true },
|
||||
.strikethrough => term.style.FontStyle{ .crossedout = true },
|
||||
.bold => term.style.FontStyle.bold,
|
||||
.italic => term.style.FontStyle.italic,
|
||||
.underline => term.style.FontStyle.underline,
|
||||
.undercurl => term.style.FontStyle.underline,
|
||||
.strikethrough => term.style.FontStyle.crossedout,
|
||||
},
|
||||
};
|
||||
try term.format.updateStyle(writer, ansi_style, null);
|
||||
|
@ -494,20 +486,6 @@ 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 {
|
||||
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 };
|
||||
try set_ansi_style(writer, reversed);
|
||||
try writer.writeAll("");
|
||||
try set_ansi_style(writer, style);
|
||||
try writer.writeAll(theme.name);
|
||||
try set_ansi_style(writer, reversed);
|
||||
try writer.writeAll("");
|
||||
try set_ansi_style(writer, plain);
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
|
||||
fn plain_cat(files: []const []const u8) !void {
|
||||
const stdout = std.io.getStdOut();
|
||||
if (files.len == 0) {
|
||||
|
@ -524,7 +502,7 @@ fn plain_cat_file(out_file: std.fs.File, in_file_name: []const u8) !void {
|
|||
try std.fs.cwd().openFile(in_file_name, .{});
|
||||
defer in_file.close();
|
||||
|
||||
var buf: [std.heap.page_size_min]u8 = undefined;
|
||||
var buf: [std.mem.page_size]u8 = undefined;
|
||||
while (true) {
|
||||
const bytes_read = try in_file.read(&buf);
|
||||
if (bytes_read == 0) return;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue