Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Allow _ and \ to match path separators #6

Merged
merged 1 commit into from
Jul 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 75 additions & 1 deletion spec/fuzzy-native-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,5 +265,79 @@ describe('fuzzy-native', function() {
expect(matcher.match('ac').length).toBe(2);
matcher.setCandidates([0, 0], ['abc', 'abc']);
expect(matcher.match('ac').length).toBe(1);
})
});

it('returns matches when using different path separators', () => {
expect(
values(matcher.match('path1_path2_path3_zzz', {caseSensitive: true}))
).toEqual([
'/path1/path2/path3/zzz',
]);
expect(
values(matcher.match('path1_path2_path3_zzz', {caseSensitive: false}))
).toEqual([
'/path1/path2/path3/zzz',
]);

expect(
values(matcher.match('\\path1\\path2\\path3\\zzz', {caseSensitive: true}))
).toEqual([
'/path1/path2/path3/zzz',
]);
expect(
values(matcher.match('\\path1\\path2\\path3\\zzz', {caseSensitive: false}))
).toEqual([
'/path1/path2/path3/zzz',
]);
});

it('returns exact matches than normalized path separator matches', () => {
matcher.setCandidates(
[0, 1, 2],
['/path1/path2/path3/zzz', '/path1/path2/path3/zzz_ooo', '/path1/path2/path3/zzz/ooo']
);

expect(
values(matcher.match('path1/path2/path3/zzz', {caseSensitive: true}))
).toEqual([
'/path1/path2/path3/zzz',
'/path1/path2/path3/zzz/ooo',
'/path1/path2/path3/zzz_ooo'
]);
expect(
values(matcher.match('zzz_ooo', {caseSensitive: true}))
).toEqual([
rafeca marked this conversation as resolved.
Show resolved Hide resolved
'/path1/path2/path3/zzz_ooo',
'/path1/path2/path3/zzz/ooo'
]);
expect(
values(matcher.match('path1/path2/path3/zzz_ooo', {caseSensitive: true}))
).toEqual([
'/path1/path2/path3/zzz_ooo',
'/path1/path2/path3/zzz/ooo'
]);
expect(
values(matcher.match('path1/path2/path3/zzz_ooo', {caseSensitive: true}))
).toEqual([
'/path1/path2/path3/zzz_ooo',
'/path1/path2/path3/zzz/ooo'
]);
expect(
values(matcher.match('path1_path2_path3_zzz_ooo', {caseSensitive: true}))
).toEqual([
'/path1/path2/path3/zzz_ooo',
'/path1/path2/path3/zzz/ooo'
]);
expect(
values(matcher.match('path1/path2_path3/zzz_ooo', {caseSensitive: false}))
).toEqual([
'/path1/path2/path3/zzz_ooo',
'/path1/path2/path3/zzz/ooo'
]);
expect(
values(matcher.match('zzz/ooo', {caseSensitive: false}))
).toEqual([
'/path1/path2/path3/zzz/ooo'
]);
});
});
2 changes: 0 additions & 2 deletions src/MatcherBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ inline uint64_t letter_bitmask(const std::string &str) {
result |= count_bit << (index * 2);
} else if (c == '-') {
result |= (1UL << 52);
} else if (c == '_') {
result |= (1UL << 53);
} else if (c >= '0' && c <= '9') {
result |= (1UL << (c - '0' + 54));
}
Expand Down
24 changes: 19 additions & 5 deletions src/score_match.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,12 @@ float recursive_match(const MatchInfo &m,
size_t last_slash = 0;
for (size_t j = haystack_idx; j <= lim; j++) {
char d = m.haystack_case[j];
if (needle_idx == 0 && (d == '/' || d == '\\')) {
bool is_path_sep = d == '/' || d == '\\';

if (needle_idx == 0 && is_path_sep) {
last_slash = j;
}
if (c == d) {
if (c == d || (is_path_sep && (c == '_' || c == '\\'))) {
// calculate score
float char_score = 1.0;
if (j > haystack_idx) {
Expand All @@ -123,8 +125,12 @@ float recursive_match(const MatchInfo &m,
}

// Apply a severe penalty if the case doesn't match.
// This should always hoist the exact case matches above all others.
if (m.smart_case && m.needle[needle_idx] != m.haystack[j]) {
// This will make the exact matches have higher score than the case
// insensitive and the path insensitive matches.
if (
(m.smart_case || m.haystack[j] == '/') &&
m.needle[needle_idx] != m.haystack[j]
) {
char_score *= 0.001;
}

Expand Down Expand Up @@ -200,7 +206,15 @@ float score_match(const char *haystack,
for (int i = m.needle_len - 1; i >= 0; i--) {
char* ptr = (char*)memrchr(m.haystack_case, m.needle_case[i], hindex);
if (ptr == nullptr) {
return 0;
// Since we treat _ and \\ as path separators, we need to re-check if path
rafeca marked this conversation as resolved.
Show resolved Hide resolved
// separator exists on the string in case they exact chars are not found.
if (m.needle_case[i] == '_' || m.needle_case[i] == '\\') {
ptr = (char*)memrchr(m.haystack_case, '/', hindex);
}

if (ptr == nullptr) {
return 0;
}
}
hindex = ptr - m.haystack_case;
last_match[i] = hindex;
Expand Down