-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathclib.zig
182 lines (164 loc) · 7.21 KB
/
clib.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
const filter = @import("filter.zig");
const std = @import("std");
const testing = std.testing;
/// rank a given string against a slice of tokens
export fn rank(
str: [*:0]const u8,
tokens: [*]const [*:0]const u8,
num_tokens: usize,
case_sensitive: bool,
plain: bool,
) f64 {
const string = std.mem.span(str);
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]);
const strict_path = filter.hasSeparator(token);
if (filter.rankToken(string, filename, token, case_sensitive, strict_path)) |r| {
total_rank += r;
} else return -1.0;
}
return total_rank;
}
/// rank a given string against a single token
export fn rankToken(
str: [*:0]const u8,
filename: ?[*:0]const u8,
token: [*:0]const u8,
case_sensitive: bool,
strict_path: bool,
) f64 {
const string = std.mem.span(str);
const name = if (filename != null) std.mem.span(filename) else null;
const tok = std.mem.span(token);
if (filter.rankToken(string, name, tok, case_sensitive, strict_path)) |r| {
return r;
} else return -1.0;
}
test "rank exported C library interface" {
{
const tokens: [2][*:0]const u8 = .{ "a", "z" };
try testing.expect(rank("abcdefg", &tokens, 2, false, false) == -1);
}
{
const tokens: [2][*:0]const u8 = .{ "a", "b" };
try testing.expect(rank("abcdefg", &tokens, 2, false, false) != -1);
}
{
const tokens: [2][*:0]const u8 = .{ "a", "B" };
try testing.expect(rank("abcdefg", &tokens, 2, true, false) == -1);
}
{
const tokens: [2][*:0]const u8 = .{ "a", "B" };
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", &tokens, 2, false, false) == -1);
}
{
const tokens: [2][*:0]const u8 = .{ "path", "file" };
try testing.expect(rank("a/path/to/file", &tokens, 2, false, false) != -1);
}
try testing.expect(rankToken("abcdefg", null, "a", false, false) != -1);
try testing.expect(rankToken("abcdefg", null, "z", false, false) == -1);
try testing.expect(rankToken("abcdefG", null, "G", true, false) != -1);
try testing.expect(rankToken("abcdefg", null, "A", true, false) == -1);
try testing.expect(rankToken("a/path/to/file", "file", "file", false, false) != -1);
try testing.expect(rankToken("a/path/to/file", "file", "zig", false, false) == -1);
// zero length strings and tokens
{
const tokens: [1][*:0]const u8 = .{"a"};
try testing.expect(rank("", &tokens, 1, false, false) == -1);
}
try testing.expect(rankToken("", null, "a", false, false) == -1);
{
const tokens: [1][*:0]const u8 = .{""};
try testing.expect(rank("a", &tokens, 1, false, false) == -1);
}
try testing.expect(rankToken("a", null, "", false, false) == -1);
}
export fn highlight(
str: [*:0]const u8,
tokens: [*]const [*:0]const u8,
tokens_len: usize,
case_sensitive: bool,
plain: bool,
matches: [*]usize,
matches_len: usize,
) usize {
const string = std.mem.span(str);
const filename = if (plain) null else std.fs.path.basename(string);
var matches_slice = matches[0..matches_len];
var index: usize = 0;
var token_index: usize = 0;
while (token_index < tokens_len) : (token_index += 1) {
const token = std.mem.span(tokens[token_index]);
const strict_path = filter.hasSeparator(token);
const matched = filter.highlightToken(string, filename, token, case_sensitive, strict_path, matches_slice[index..]);
index += matched.len;
}
return index;
}
export fn highlightToken(
str: [*:0]const u8,
filename: ?[*:0]const u8,
token: [*:0]const u8,
case_sensitive: bool,
strict_path: bool,
matches: [*]usize,
matches_len: usize,
) usize {
const string = std.mem.span(str);
const name = if (filename != null) std.mem.span(filename) else null;
const tok = std.mem.span(token);
var matches_slice = matches[0..matches_len];
const matched = filter.highlightToken(string, name, tok, case_sensitive, strict_path, matches_slice);
return matched.len;
}
fn testHighlight(
expectedMatches: []const usize,
str: [*:0]const u8,
tokens: []const [*:0]const u8,
case_sensitive: bool,
plain: bool,
matches_buf: []usize,
) !void {
const len = highlight(str, tokens.ptr, tokens.len, case_sensitive, plain, matches_buf.ptr, matches_buf.len);
try testing.expectEqualSlices(usize, expectedMatches, matches_buf[0..len]);
}
test "highlight exported C library interface" {
var matches_buf: [128]usize = undefined;
try testHighlight(&.{ 0, 5 }, "abcdef", &.{ "a", "f" }, false, false, &matches_buf);
try testHighlight(&.{ 0, 5 }, "abcdeF", &.{ "a", "F" }, true, false, &matches_buf);
try testHighlight(&.{ 2, 3, 4, 5, 10, 11, 12, 13 }, "a/path/to/file", &.{ "path", "file" }, false, false, &matches_buf);
var len = highlightToken("abcdef", null, "a", false, false, &matches_buf, matches_buf.len);
try testing.expectEqualSlices(usize, &.{0}, matches_buf[0..len]);
len = highlightToken("abcdeF", null, "F", true, false, &matches_buf, matches_buf.len);
try testing.expectEqualSlices(usize, &.{5}, matches_buf[0..len]);
len = highlightToken("a/path/to/file", "file", "file", false, false, &matches_buf, matches_buf.len);
try testing.expectEqualSlices(usize, &.{ 10, 11, 12, 13 }, matches_buf[0..len]);
// highlights with basename trailing slashes
len = highlightToken("s/", "s", "s", false, false, &matches_buf, matches_buf.len);
try testing.expectEqualSlices(usize, &.{0}, matches_buf[0..len]);
len = highlightToken("/this/is/path/not/a/file/", "file", "file", false, false, &matches_buf, matches_buf.len);
try testing.expectEqualSlices(usize, &.{ 20, 21, 22, 23 }, matches_buf[0..len]);
// disconnected highlights
try testHighlight(&.{ 0, 2, 3 }, "ababab", &.{"aab"}, false, false, &matches_buf);
try testHighlight(&.{ 6, 8, 9 }, "abbbbbabab", &.{"aab"}, false, false, &matches_buf);
try testHighlight(&.{ 0, 2, 6 }, "abcdefg", &.{"acg"}, false, false, &matches_buf);
try testHighlight(&.{ 2, 3, 4, 5, 9, 10 }, "__init__.py", &.{"initpy"}, false, false, &matches_buf);
// small buffer to ensure highlighting doesn't go out of range when the tokens overflow
var small_buf: [4]usize = undefined;
try testHighlight(&.{ 0, 1, 2, 3 }, "abcd", &.{ "ab", "cd", "abcd" }, false, false, &small_buf);
try testHighlight(&.{ 0, 1, 2, 1 }, "wxyz", &.{ "wxy", "xyz" }, false, false, &small_buf);
// zero length strings and tokens
try testHighlight(&.{}, "", &.{"a"}, false, false, &matches_buf);
len = highlightToken("", null, "a", false, false, &matches_buf, matches_buf.len);
try testing.expectEqualSlices(usize, &.{}, matches_buf[0..len]);
try testHighlight(&.{}, "a", &.{""}, false, false, &matches_buf);
len = highlightToken("a", null, "", false, false, &matches_buf, matches_buf.len);
try testing.expectEqualSlices(usize, &.{}, matches_buf[0..len]);
}