find.lua (3434B)
1 --[[ 2 This file is an internal file-browser addon. 3 It should not be imported like a normal module. 4 5 Allows searching the current directory. 6 ]]-- 7 8 local msg = require "mp.msg" 9 local fb = require "file-browser" 10 local input_loaded, input = pcall(require, "mp.input") 11 local user_input_loaded, user_input = pcall(require, "user-input-module") 12 13 ---@type ParserConfig 14 local find = { 15 api_version = "1.3.0" 16 } 17 18 ---@type thread|nil 19 local latest_coroutine = nil 20 21 ---@type State 22 local global_fb_state = getmetatable(fb.get_state()).__original 23 24 ---@param name string 25 ---@param query string 26 ---@return boolean 27 local function compare(name, query) 28 if name:find(query) then return true end 29 if name:lower():find(query) then return true end 30 if name:upper():find(query) then return true end 31 32 return false 33 end 34 35 ---@async 36 ---@param key Keybind 37 ---@param state State 38 ---@param co thread 39 ---@return boolean? 40 local function main(key, state, co) 41 if not state.list then return false end 42 43 ---@type string 44 local text 45 if key.name == "find/find" then text = "Find: enter search string" 46 else text = "Find: enter advanced search string" end 47 48 if input_loaded then 49 input.get({ 50 prompt = text .. "\n>", 51 id = "file-browser/find", 52 submit = fb.coroutine.callback(), 53 }) 54 elseif user_input_loaded then 55 user_input.get_user_input( fb.coroutine.callback(), { text = text, id = "find", replace = true } ) 56 end 57 58 local query, error = coroutine.yield() 59 if input_loaded then input.terminate() end 60 if not query then return msg.debug(error) end 61 62 -- allow the directory to be changed before this point 63 local list = fb.get_list() 64 local parse_id = global_fb_state.co 65 66 if key.name == "find/find" then 67 query = fb.pattern_escape(query) 68 end 69 70 local results = {} 71 72 for index, item in ipairs(list) do 73 if compare(item.label or item.name, query) then 74 table.insert(results, index) 75 end 76 end 77 78 if (#results < 1) then 79 msg.warn("No matching items for '"..query.."'") 80 return 81 end 82 83 --keep cycling through the search results if any are found 84 --putting this into a separate coroutine removes any passthrough ambiguity 85 --the final return statement should return to `step_find` not any other function 86 ---@async 87 fb.coroutine.run(function() 88 latest_coroutine = coroutine.running() 89 ---@type number 90 local rindex = 1 91 while (true) do 92 93 if rindex == 0 then rindex = #results 94 elseif rindex == #results + 1 then rindex = 1 end 95 96 fb.set_selected_index(results[rindex]) 97 local direction = coroutine.yield(true) --[[@as number]] 98 rindex = rindex + direction 99 100 if parse_id ~= global_fb_state.co then 101 latest_coroutine = nil 102 return 103 end 104 end 105 end) 106 end 107 108 local function step_find(key) 109 if not latest_coroutine then return false end 110 ---@type number 111 local direction = 0 112 if key.name == "find/next" then direction = 1 113 elseif key.name == "find/prev" then direction = -1 end 114 return fb.coroutine.resume_err(latest_coroutine, direction) 115 end 116 117 find.keybinds = { 118 {"Ctrl+f", "find", main, {}}, 119 {"Ctrl+F", "find_advanced", main, {}}, 120 {"n", "next", step_find, {}}, 121 {"N", "prev", step_find, {}}, 122 } 123 124 return find