diff --git a/src/clib.zig b/src/clib.zig index 969868b..00246ee 100644 --- a/src/clib.zig +++ b/src/clib.zig @@ -5,19 +5,19 @@ const testing = std.testing; /// rank a given string against a slice of tokens export fn rank( str: [*:0]const u8, - filename: ?[*:0]const u8, tokens: [*]const [*:0]const u8, num_tokens: usize, case_sensitive: bool, + plain: bool, ) f64 { const string = std.mem.span(str); - const name = if (filename != null) std.mem.span(filename) else null; + const filename = if (plain) null else std.fs.path.basename(string); var total_rank: f64 = 0; var index: usize = 0; while (index < num_tokens) : (index += 1) { const token = std.mem.span(tokens[index]); - if (filter.rankToken(string, name, token, case_sensitive)) |r| { + if (filter.rankToken(string, filename, token, case_sensitive)) |r| { total_rank += r; } else return -1.0; } @@ -42,27 +42,27 @@ export fn rankToken( test "rank exported C library interface" { { const tokens: [2][*:0]const u8 = .{ "a", "z" }; - try testing.expect(rank("abcdefg", null, &tokens, 2, false) == -1); + try testing.expect(rank("abcdefg", &tokens, 2, false, false) == -1); } { const tokens: [2][*:0]const u8 = .{ "a", "b" }; - try testing.expect(rank("abcdefg", null, &tokens, 2, false) != -1); + try testing.expect(rank("abcdefg", &tokens, 2, false, false) != -1); } { const tokens: [2][*:0]const u8 = .{ "a", "B" }; - try testing.expect(rank("abcdefg", null, &tokens, 2, true) == -1); + try testing.expect(rank("abcdefg", &tokens, 2, true, false) == -1); } { const tokens: [2][*:0]const u8 = .{ "a", "B" }; - try testing.expect(rank("aBcdefg", null, &tokens, 2, true) != -1); + try testing.expect(rank("aBcdefg", &tokens, 2, true, false) != -1); } { const tokens: [1][*:0]const u8 = .{"zig"}; - try testing.expect(rank("a/path/to/file", null, &tokens, 2, false) == -1); + try testing.expect(rank("a/path/to/file", &tokens, 2, false, false) == -1); } { const tokens: [2][*:0]const u8 = .{ "path", "file" }; - try testing.expect(rank("a/path/to/file", "file", &tokens, 2, false) != -1); + try testing.expect(rank("a/path/to/file", &tokens, 2, false, false) != -1); } try testing.expect(rankToken("abcdefg", null, "a", false) != -1); @@ -77,19 +77,19 @@ const Range = filter.Range; export fn highlight( str: [*:0]const u8, - filename: ?[*:0]const u8, ranges: [*]Range, tokens: [*]const [*:0]const u8, num: usize, case_sensitive: bool, + plain: bool, ) void { const string = std.mem.span(str); - const name = if (filename != null) std.mem.span(filename) else null; + const filename = if (plain) null else std.fs.path.basename(string); var index: usize = 0; while (index < num) : (index += 1) { const token = std.mem.span(tokens[index]); - ranges[index] = filter.highlightToken(string, name, token, case_sensitive); + ranges[index] = filter.highlightToken(string, filename, token, case_sensitive); } } @@ -108,20 +108,20 @@ export fn highlightToken( fn testHighlight( expectedRanges: []const Range, str: [*:0]const u8, - filename: ?[*:0]const u8, tokens: []const [*:0]const u8, case_sensitive: bool, + plain: bool, ) !void { var ranges = try testing.allocator.alloc(Range, tokens.len); defer testing.allocator.free(ranges); - highlight(str, filename, ranges.ptr, tokens.ptr, ranges.len, case_sensitive); + highlight(str, ranges.ptr, tokens.ptr, ranges.len, case_sensitive, plain); try testing.expectEqualSlices(Range, expectedRanges, ranges); } test "highlight exported C library interface" { - try testHighlight(&.{ .{ .start = 0, .end = 0 }, .{ .start = 5, .end = 5 } }, "abcdef", null, &.{ "a", "f" }, false); - try testHighlight(&.{ .{ .start = 0, .end = 0 }, .{ .start = 5, .end = 5 } }, "abcdeF", null, &.{ "a", "F" }, true); - try testHighlight(&.{ .{ .start = 2, .end = 5 }, .{ .start = 10, .end = 13 } }, "a/path/to/file", "file", &.{ "path", "file" }, false); + try testHighlight(&.{ .{ .start = 0, .end = 0 }, .{ .start = 5, .end = 5 } }, "abcdef", &.{ "a", "f" }, false, false); + try testHighlight(&.{ .{ .start = 0, .end = 0 }, .{ .start = 5, .end = 5 } }, "abcdeF", &.{ "a", "F" }, true, false); + try testHighlight(&.{ .{ .start = 2, .end = 5 }, .{ .start = 10, .end = 13 } }, "a/path/to/file", &.{ "path", "file" }, false, false); try testing.expectEqual(Range{ .start = 0, .end = 0 }, highlightToken("abcdef", null, "a", false)); try testing.expectEqual(Range{ .start = 5, .end = 5 }, highlightToken("abcdeF", null, "F", true)); diff --git a/src/filter.zig b/src/filter.zig index 714fdbc..39306e3 100644 --- a/src/filter.zig +++ b/src/filter.zig @@ -150,7 +150,7 @@ const IndexIterator = struct { /// rank a candidate against the given query tokens /// /// algorithm inspired by https://github.com/garybernhardt/selecta -fn rankCandidate( +pub fn rankCandidate( candidate: []const u8, query_tokens: []const []const u8, case_sensitive: bool, diff --git a/src/lib.zig b/src/lib.zig index 4d1d8e2..30aea12 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -5,17 +5,11 @@ const testing = std.testing; /// rank a given string against a slice of tokens pub fn rank( str: []const u8, - filename: ?[]const u8, tokens: []const []const u8, case_sensitive: bool, -) f64 { - var total_rank: f64 = 0; - for (tokens) |token| { - if (filter.rankToken(str, filename, token, case_sensitive)) |r| { - total_rank += r; - } else return -1.0; - } - return total_rank; + plain: bool, +) ?f64 { + return filter.rankCandidate(str, tokens, case_sensitive, plain); } /// rank a given string against a single token @@ -29,12 +23,12 @@ pub fn rankToken( } test "rank library interface" { - try testing.expect(rank("abcdefg", null, &.{ "a", "z" }, false) == -1); - try testing.expect(rank("abcdefg", null, &.{ "a", "b" }, false) != -1); - try testing.expect(rank("abcdefg", null, &.{ "a", "B" }, true) == -1); - try testing.expect(rank("aBcdefg", null, &.{ "a", "B" }, true) != -1); - try testing.expect(rank("a/path/to/file", "file", &.{"zig"}, false) == -1); - try testing.expect(rank("a/path/to/file", "file", &.{ "path", "file" }, false) != -1); + try testing.expect(rank("abcdefg", &.{ "a", "z" }, false, false) == null); + try testing.expect(rank("abcdefg", &.{ "a", "b" }, false, false) != null); + try testing.expect(rank("abcdefg", &.{ "a", "B" }, true, false) == null); + try testing.expect(rank("aBcdefg", &.{ "a", "B" }, true, false) != null); + try testing.expect(rank("a/path/to/file", &.{"zig"}, false, false) == null); + try testing.expect(rank("a/path/to/file", &.{ "path", "file" }, false, false) != null); try testing.expect(rankToken("abcdefg", null, "a", false) != null); try testing.expect(rankToken("abcdefg", null, "z", false) == null); @@ -50,11 +44,12 @@ pub const Range = filter.Range; /// compute matching ranges given a string and a slice of tokens pub fn highlight( str: []const u8, - filename: ?[]const u8, ranges: []Range, tokens: []const []const u8, case_sensitive: bool, + plain: bool, ) void { + const filename = if (plain) null else std.fs.path.basename(str); for (tokens) |token, i| { ranges[i] = filter.highlightToken(str, filename, token, case_sensitive); } @@ -73,20 +68,20 @@ pub fn highlightToken( fn testHighlight( expectedRanges: []const Range, str: []const u8, - filename: ?[]const u8, tokens: []const []const u8, case_sensitive: bool, + plain: bool, ) !void { var ranges = try testing.allocator.alloc(Range, tokens.len); defer testing.allocator.free(ranges); - highlight(str, filename, ranges, tokens, case_sensitive); + highlight(str, ranges, tokens, case_sensitive, plain); try testing.expectEqualSlices(Range, expectedRanges, ranges); } test "highlight library interface" { - try testHighlight(&.{ .{ .start = 0, .end = 0 }, .{ .start = 5, .end = 5 } }, "abcdef", null, &.{ "a", "f" }, false); - try testHighlight(&.{ .{ .start = 0, .end = 0 }, .{ .start = 5, .end = 5 } }, "abcdeF", null, &.{ "a", "F" }, true); - try testHighlight(&.{ .{ .start = 2, .end = 5 }, .{ .start = 10, .end = 13 } }, "a/path/to/file", "file", &.{ "path", "file" }, false); + try testHighlight(&.{ .{ .start = 0, .end = 0 }, .{ .start = 5, .end = 5 } }, "abcdef", &.{ "a", "f" }, false, false); + try testHighlight(&.{ .{ .start = 0, .end = 0 }, .{ .start = 5, .end = 5 } }, "abcdeF", &.{ "a", "F" }, true, false); + try testHighlight(&.{ .{ .start = 2, .end = 5 }, .{ .start = 10, .end = 13 } }, "a/path/to/file", &.{ "path", "file" }, false, false); try testing.expectEqual(Range{ .start = 0, .end = 0 }, highlightToken("abcdef", null, "a", false)); try testing.expectEqual(Range{ .start = 5, .end = 5 }, highlightToken("abcdeF", null, "F", true));