branch: externals/hotfuzz
commit a19395aca9eff0e31c51efbbe4c6e16229f3b380
Author: Axel Forsman <[email protected]>
Commit: Axel Forsman <[email protected]>
Obey completion-ignore-case in hotfuzz--filter-c
`hotfuzz--filter-c` always being case-insensitive posed a problem since
`hotfuzz-highlight` would try to highlight the completions according to
`completion-ignore-case` irrespective of whether a completion had
matched only case-insensitively.
Resolves #5
---
README.md | 3 +--
hotfuzz-module.c | 41 ++++++++++++++++++++---------------------
hotfuzz.el | 3 ++-
test/tests.el | 6 +++---
4 files changed, 26 insertions(+), 27 deletions(-)
diff --git a/README.md b/README.md
index 64a0c6c465..1b9b1a245d 100644
--- a/README.md
+++ b/README.md
@@ -59,8 +59,7 @@ cmake -DCMAKE_C_FLAGS='-O3 -march=native' .. \
and place the resulting shared library somewhere in `load-path`.
Unlike the Lisp implementation,
-the dynamic module uses an unstable sorting algorithm
-and is always case-insensitive.
+the dynamic module uses an unstable sorting algorithm.
## Related projects
diff --git a/hotfuzz-module.c b/hotfuzz-module.c
index bc576e4848..46773ab8c3 100644
--- a/hotfuzz-module.c
+++ b/hotfuzz-module.c
@@ -66,7 +66,7 @@ static void match_row(struct EmacsStr *a, struct EmacsStr *b,
cost *bonuses, uns
}
}
-static cost get_cost(struct EmacsStr *needle, struct EmacsStr *haystack) {
+static cost get_cost(struct EmacsStr *needle, struct EmacsStr *haystack, bool
ignore_case) {
unsigned n = haystack->len, m = needle->len;
if (n > MAX_HAYSTACK_LEN || m > MAX_NEEDLE_LEN) return 10000;
cost c[MAX_NEEDLE_LEN], d[MAX_NEEDLE_LEN];
@@ -76,7 +76,8 @@ static cost get_cost(struct EmacsStr *needle, struct EmacsStr
*haystack) {
calc_bonus(haystack, bonuses);
for (unsigned i = 0; i < n; ++i) {
- haystack->b[i] = tolower(haystack->b[i]);
+ if (ignore_case)
+ haystack->b[i] = tolower(haystack->b[i]);
match_row(haystack, needle, bonuses, i, c, d, c, d);
}
@@ -84,17 +85,17 @@ static cost get_cost(struct EmacsStr *needle, struct
EmacsStr *haystack) {
}
/**
- * Returns whether haystack case-insensitively matches needle.
- *
- * This function does not take the value of completion-ignore-case
- * into account.
+ * Returns whether @p haystack matches @p needle.
*
* @param needle Null-terminated search string.
* @param haystack Null-terminated completion candidate.
+ * @param ignore_case Whether to match case-insensitively.
*/
-static bool is_match(char *needle, char *haystack) {
+static bool is_match(char *needle, char *haystack, bool ignore_case) {
while (*needle) {
- if ((haystack = strpbrk(haystack, (char[]) { *needle,
toupper(*needle), '\0' })))
+ if (ignore_case
+ ? (haystack = strpbrk(haystack, (char[]) { *needle,
toupper(*needle), '\0' }))
+ : (haystack = strchr(haystack, *needle)))
++needle, ++haystack; // Skip past matched character
else
return false;
@@ -174,6 +175,7 @@ struct Batch {
struct Shared {
pthread_mutex_t mutex;
+ bool ignore_case;
struct EmacsStr *needle;
struct Batch *batches, *batches_end;
};
@@ -199,10 +201,10 @@ static void *worker_routine(void *ptr) {
unsigned num_matches = 0;
for (unsigned i = 0; i < batch->len; ++i) {
struct Candidate *candidate = batch->xs + i;
- if (!is_match(needle->b, candidate->s->b)) continue;
+ if (!is_match(needle->b, candidate->s->b,
shared->ignore_case)) continue;
batch->xs[num_matches++] = (struct Candidate) {
.s = candidate->s,
- .key = get_cost(needle, candidate->s),
+ .key = get_cost(needle, candidate->s,
shared->ignore_case),
};
}
batch->len = num_matches;
@@ -222,13 +224,7 @@ struct Data {
struct Worker *workers;
};
-emacs_value hotfuzz_filter(emacs_env *env, ptrdiff_t nargs __attribute__
((__unused__)), emacs_value args[], void *data_ptr) {
- // Short-circuit if needle is empty
- ptrdiff_t needle_len;
- env->copy_string_contents(env, args[0], NULL, &needle_len);
- if (needle_len == /* solely null byte */ 1)
- return args[1];
-
+emacs_value hotfuzz_filter(emacs_env *env, ptrdiff_t nargs, emacs_value
args[], void *data_ptr) {
struct Data *data = data_ptr;
emacs_value fcar = env->intern(env, "car"),
fcdr = env->intern(env, "cdr"),
@@ -262,11 +258,14 @@ emacs_value hotfuzz_filter(emacs_env *env, ptrdiff_t
nargs __attribute__ ((__unu
}
if (!batches) return nil;
+ bool ignore_case = nargs >= 3 && env->is_not_nil(env, args[2]);
struct EmacsStr *needle = copy_emacs_string(env, &bump, args[0]);
if (!needle) goto error;
- for (unsigned i = 0; i < needle->len; ++i)
- needle->b[i] = tolower(needle->b[i]);
+ if (ignore_case)
+ for (size_t i = 0; i < needle->len; ++i)
+ needle->b[i] = tolower(needle->b[i]);
struct Shared shared = {
+ .ignore_case = ignore_case,
.needle = needle,
.batches = batches,
.batches_end = batches + batch_idx + 1,
@@ -340,10 +339,10 @@ int emacs_module_init(struct emacs_runtime *rt) {
env->funcall(env, env->intern(env, "defalias"), 2, (emacs_value[]) {
env->intern(env, "hotfuzz--filter-c"),
- env->make_function(env, 2, 2, hotfuzz_filter,
+ env->make_function(env, 2, 3, hotfuzz_filter,
"Filter and sort
CANDIDATES that match STRING.\n"
"\n"
- "\(fn STRING
CANDIDATES)",
+ "\(fn STRING
CANDIDATES &optional IGNORE-CASE)",
&data),
});
diff --git a/hotfuzz.el b/hotfuzz.el
index c95859987c..7445128958 100644
--- a/hotfuzz.el
+++ b/hotfuzz.el
@@ -149,7 +149,8 @@ HAYSTACK has to be a match according to `hotfuzz-filter'."
CANDIDATES should be a list of strings."
(cond
((not (<= 1 (length string) hotfuzz--max-needle-len)) candidates)
- ((featurep 'hotfuzz-module) (hotfuzz--filter-c string candidates))
+ ((featurep 'hotfuzz-module)
+ (hotfuzz--filter-c string candidates completion-ignore-case))
((let ((re (concat
"\\`"
(mapconcat
diff --git a/test/tests.el b/test/tests.el
index b2d7da2726..e31b6c844f 100644
--- a/test/tests.el
+++ b/test/tests.el
@@ -43,10 +43,10 @@
;;; Filtering tests
(ert-deftest case-sensitivity-test ()
- (let ((xs '("aa" "aA" "Aa" "AA")))
+ (let ((xs '("aa" "aA " "Aa " "AA ")))
(let ((completion-ignore-case nil))
- (should (equal (hotfuzz-filter "a" xs) '("aa" "aA" "Aa")))
- (should (equal (hotfuzz-filter "A" xs) '("Aa" "AA" "aA"))))
+ (should (equal (hotfuzz-filter "a" xs) '("aa" "aA " "Aa ")))
+ (should (equal (hotfuzz-filter "A" xs) '("Aa " "AA " "aA "))))
(let ((completion-ignore-case t))
(should (equal (hotfuzz-filter "a" xs) xs))
(should (equal (hotfuzz-filter "A" xs) xs)))))