From 2f84ce6b5df1a5a70735157b3ddc3129102066e6 Mon Sep 17 00:00:00 2001 From: Nathan Craddock Date: Wed, 22 Mar 2023 20:21:48 -0600 Subject: [PATCH] feat(#35): add -d and --delimiter options Adds a new option for for setting a custom delimiter. Currently only supports escaping \n and \0 and any other delimiter only considers the first byte. Closes #35 --- src/filter.zig | 2 +- src/main.zig | 39 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/filter.zig b/src/filter.zig index 116ff06..54bcf85 100644 --- a/src/filter.zig +++ b/src/filter.zig @@ -28,7 +28,7 @@ pub fn collectCandidates(allocator: std.mem.Allocator, buf: []const u8, delimite } // catch the end if stdio didn't end in a delimiter - if (start < buf.len) { + if (start < buf.len and buf[start] != delimiter) { try candidates.append(buf[start..]); } diff --git a/src/main.zig b/src/main.zig index 6e3f8e6..a2585dc 100644 --- a/src/main.zig +++ b/src/main.zig @@ -20,6 +20,7 @@ const version_str = std.fmt.comptimePrint("zf {s} Nathan Craddock", .{version}); const help = \\Usage: zf [options] \\ + \\-d, --delimiter Set the delimiter used to split candidates (default \n) \\-f, --filter Skip interactive use and filter using the given query \\-k, --keep-order Don't sort by rank and preserve order of lines read on stdin \\-l, --lines Set the maximum number of result lines to show (default 10) @@ -36,6 +37,7 @@ const Config = struct { lines: usize = 10, plain: bool = false, query: []u8 = undefined, + delimiter: []const u8 = "\n", // HACK: error unions cannot return a value, so return error messages in // the config struct instead @@ -95,6 +97,29 @@ fn parseArgs(allocator: std.mem.Allocator, args: []const []const u8) !Config { config.query = try allocator.alloc(u8, args[index + 1].len); std.mem.copy(u8, config.query, args[index + 1]); + skip = true; + } else if (eql(u8, arg, "-d") or eql(u8, arg, "--delimiter")) { + if (index + 1 > args.len - 1) { + config.err = true; + config.err_str = try std.fmt.allocPrint( + allocator, + "zf: option '{s}' requires an argument\n{s}", + .{ arg, help }, + ); + return config; + } + + config.delimiter = args[index + 1]; + if (config.delimiter.len == 0) { + config.err = true; + config.err_str = try std.fmt.allocPrint( + allocator, + "zf: delimiter cannot be empty\n{s}", + .{ help }, + ); + return config; + } + skip = true; } else { config.err = true; @@ -224,10 +249,20 @@ pub fn main() anyerror!void { const buf = blk: { var stdin = io.getStdIn().reader(); const buf = try readAll(allocator, &stdin); - break :blk (try normalizer.nfd(allocator, buf)).slice; + break :blk std.mem.trim(u8, (try normalizer.nfd(allocator, buf)).slice, "\n"); + }; + + // escape specific delimiters + const delimiter = blk: { + if (eql(u8, config.delimiter, "\\n")) { + break :blk '\n'; + } else if (eql(u8, config.delimiter, "\\0")) { + break :blk 0; + } else { + break :blk config.delimiter[0]; + } }; - const delimiter = '\n'; var candidates = try filter.collectCandidates(allocator, buf, delimiter); if (candidates.len == 0) std.process.exit(1);