diff --git a/DOCS/interface-changes/console-exact_match.txt b/DOCS/interface-changes/console-exact_match.txt new file mode 100644 index 0000000000..d979aed26c --- /dev/null +++ b/DOCS/interface-changes/console-exact_match.txt @@ -0,0 +1 @@ +add `console-exact_match` script-opt diff --git a/DOCS/man/console.rst b/DOCS/man/console.rst index aa725b396e..a636b0735b 100644 --- a/DOCS/man/console.rst +++ b/DOCS/man/console.rst @@ -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. diff --git a/DOCS/man/select.rst b/DOCS/man/select.rst index 0ecb2f9b96..9bca439d1f 100644 --- a/DOCS/man/select.rst +++ b/DOCS/man/select.rst @@ -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 --------------- diff --git a/player/lua/console.lua b/player/lua/console.lua index 30610c65e3..817f51b5f8 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -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]],