console.lua: implement exact matching

Allow matching items exactly instead of fuzzily when prefixing the
search with ' (like in fzf) or with
--script-opt=console-exact_match=yes.

This is mainly useful to filter history entries chronologically.

You can also specify multiple search terms delimited by spaces. But '
makes every term an exact match, unlike fzf where you need to place it
before every word, since we can't hook into fzy's algorithm.

Closes #14587.
This commit is contained in:
Guido Cella authored and Kacper Michajłow committed 2025-04-17 00:01:27 +02:00
1 parent bff0d59cbe
commit 4cb5f7aa1f
4 files changed
+65 -4

No files matched your search

@@ -0,0 +1 @@
add `console-exact_match` script-opt
+6
View File
@@ -199,6 +199,12 @@ Configurable Options
The color of characters that match the searched string.
``exact_match``
Default: no
Whether to match menu search queries exactly instead of fuzzily. Without
this option, prefixing queries with ``'`` enables exact matching.
``case_sensitive``
Default: no on Windows, yes on other platforms.
+8 -3
View File
@@ -10,9 +10,8 @@ console and do operations on the selected item. It can be disabled using the
Key bindings
------------
When using ``mp.input.select``, typing printable characters does a fuzzy search
of the presented items, and key bindings listed in `CONSOLE`_ are extended with
the following:
When using ``mp.input.select``, the key bindings listed in `CONSOLE`_ are
extended with the following:
ENTER, Ctrl+j and Ctrl+m
Confirm the selection of the highlighted item.
@@ -39,6 +38,12 @@ WHEEL_UP
WHEEL_DOWN
Scroll down.
Typing printable characters does a fuzzy search of the presented items.
If the query starts with ``'``, only exact matches are filtered. You can also
specify multiple search terms delimited by spaces, and only items matching all
terms are filtered.
Script bindings
---------------
+50 -1
View File
@@ -43,6 +43,7 @@ local opts = {
selected_color = "#222222",
selected_back_color = "#FFFFFF",
match_color = "#0088FF",
exact_match = false,
case_sensitive = platform ~= "windows" and true or false,
history_dedup = true,
font_hw_ratio = "auto",
@@ -464,6 +465,54 @@ local function fuzzy_find(needle, haystacks, case_sensitive)
return result
end
local function find_matches(needle, haystacks)
if not opts.exact_match and needle:sub(1, 1) ~= "'" then
return fuzzy_find(needle, haystacks)
end
if not opts.exact_match then
needle = needle:sub(2)
end
if not opts.case_sensitive then
needle = needle:lower()
end
local result = {}
local needle_words = {}
for word in needle:gmatch("%S+") do
needle_words[#needle_words + 1] = word
end
for i, haystack in ipairs(haystacks) do
if not opts.case_sensitive then
haystack = haystack:lower()
end
local matching_positions = {}
for _, word in pairs(needle_words) do
local start, e = haystack:find(word, 1, true)
if start then
for j = start, e do
matching_positions[#matching_positions + 1] = j
end
else
matching_positions = nil
break
end
end
if matching_positions then
table.sort(matching_positions)
result[#result + 1] = { i, matching_positions }
end
end
return result
end
local function get_matches_to_print(terminal)
if not selectable_items or selected_match == 0 then
return {}
@@ -886,7 +935,7 @@ local function handle_edit()
end
matches = {}
for i, match in ipairs(fuzzy_find(line, selectable_items)) do
for i, match in ipairs(find_matches(line, selectable_items)) do
matches[i] = {
index = match[1],
text = selectable_items[match[1]],