: nply/wmenu

commit b8e79aeac945364a70f17f3f8e6faffd25da238e
parent 3bd664498600d92ca1be6e01e35311bceea2e977
Author: Bharatvaj Hemanth <bharatvaj@yahoo.com>
Date: Tue, 13 Oct 2026 23:15:44 +0530

Make -i enable ASCII case-insensitive matching
4 files changed, 12 insertions(+), 5 deletions(-)
M
HACKING
|
5
+++--
M
README
|
1
+
M
test_stream.c
|
6
+++++-
M
wmenu.c
|
5
+++--
diff --git a/HACKING b/HACKING
@@ -13,8 +13,9 @@ Build
 Dependencies
 ------------
 No external source dependencies. The byte-safe subsequence matcher lives in
-wmenu.c; ASCII matching retains fzy's old behavior, while non-ASCII UTF-8
-bytes are compared literally.
+wmenu.c; ASCII matching retains fzy's smart-case behavior by default, and
+-i forces ASCII case-insensitive matching. Non-ASCII UTF-8 bytes compare
+literally.
 
 Program shape
 -------------
diff --git a/README b/README
@@ -29,6 +29,7 @@ Useful flags:
 -c                  center window
 -l lines            visible list lines; -l-1 fills the screen (default 5)
 -m                  select multiple entries; Ctrl+Enter adds one
+-i                  ignore ASCII case while matching
 -V                  enable vi support; Ctrl+Alt+V toggles vi mode
 -d                  use normal Windows edit shortcuts instead of emacs-ish keys
 -b                  exit when focus leaves the window
diff --git a/test_stream.c b/test_stream.c
@@ -22,7 +22,11 @@ int main(int argc, char **argv)
     assert(fit_lines(0, 300) == 5);
     assert(fit_lines(7, 35) == 1);
     assert(has_match("aC", "ABC"));
-    assert(!has_match("Ab", "abc")); /* Preserve fzy's ASCII query casing. */
+    assert(!has_match("Ab", "abc")); /* Default smart case. */
+    ignore_case = true;
+    assert(has_match("Ab", "abc")); /* -i also folds uppercase query bytes. */
+    assert(has_match("AB", "alphabet"));
+    ignore_case = false;
     assert(has_match("\xc3\xa9", "caf\xc3\xa9"));
     assert(!has_match("\xc3\xa9", "caf\xc3\x89"));
     FILE *input = tmpfile();
diff --git a/wmenu.c b/wmenu.c
@@ -42,7 +42,7 @@ static struct location locations[LOCATION_LIMIT];
 static int location_count, location_cursor, last_row = -1, pending_item = -1;
 static bool restoring_location, search_posted;
 static volatile LONG refresh_pending;
-static bool has_items, has_prompt, multi_select, vi_mode, normal_mode, default_controls;
+static bool has_items, has_prompt, multi_select, vi_mode, normal_mode, default_controls, ignore_case;
 static wchar_t prompt[300];
 static HWND main_hwnd, input_hwnd, list_hwnd, progress_hwnd;
 static WNDPROC old_input_proc, old_list_proc;

@@ -188,6 +188,7 @@ static bool has_match(const char *query, const char *line)
 {
     while (*query) {
         unsigned char ch = (unsigned char)*query++;
+        if (ignore_case && ch >= 'A' && ch <= 'Z') ch = ch - 'A' + 'a';
         unsigned char upper = ch >= 'a' && ch <= 'z' ? ch - 'a' + 'A' : ch;
         while (*line && (unsigned char)*line != ch && (unsigned char)*line != upper) ++line;
         if (!*line) return false;

@@ -839,7 +840,7 @@ int main(int argc, char **argv)
     case 'V': vi_mode = true; break;
     case 'd': default_controls = true; break;
     case 'b': close_on_blur = true; break;
-    case 'i': break;
+    case 'i': ignore_case = true; break;
     default: usage(); return 1;
     } ARGEND;