directory-movement.lua (7433B)
1 2 local mp = require 'mp' 3 local msg = require 'mp.msg' 4 local utils = require 'mp.utils' 5 6 local o = require 'modules.options' 7 local g = require 'modules.globals' 8 local ass = require 'modules.ass' 9 local scanning = require 'modules.navigation.scanning' 10 local fb_utils = require 'modules.utils' 11 local cursor = require 'modules.navigation.cursor' 12 13 ---@class directory_movement 14 local directory_movement = {} 15 local NavType = scanning.NavType 16 17 ---Appends an item to the directory stack, wiping any 18 ---directories further ahead than the current position. 19 ---@param dir string 20 local function directory_stack_append(dir) 21 -- don't clear the stack if we're re-entering the same directory 22 if g.directory_stack.stack[g.directory_stack.position + 1] == dir then 23 g.directory_stack.position = g.directory_stack.position + 1 24 return 25 end 26 27 local j = #g.directory_stack.stack 28 while g.directory_stack.position < j do 29 g.directory_stack.stack[j] = nil 30 j = j - 1 31 end 32 table.insert(g.directory_stack.stack, dir) 33 g.directory_stack.position = g.directory_stack.position + 1 34 end 35 36 ---@param dir string 37 local function directory_stack_prepend(dir) 38 table.insert(g.directory_stack.stack, 1, dir) 39 g.directory_stack.position = 1 40 end 41 42 ---Clears directories from the history 43 ---@param from? number All entries >= this index are cleared. 44 ---@return string[] 45 function directory_movement.clear_history(from) 46 ---@type string[] 47 local cleared = {} 48 49 from = from or 1 50 for i = g.history.size, from, -1 do 51 table.insert(cleared, g.history.list[i]) 52 g.history.list[i] = nil 53 g.history.size = g.history.size - 1 54 55 if g.history.position >= i then 56 g.history.position = g.history.position - 1 57 end 58 end 59 60 return cleared 61 end 62 63 ---Append a directory to the history 64 ---If we have navigated backward in the history, 65 ---then clear any history beyond the current point. 66 ---@param directory string 67 function directory_movement.append_history(directory) 68 if g.history.list[g.history.position] == directory then 69 msg.debug('reloading same directory - history unchanged:', directory) 70 return 71 end 72 73 msg.debug('appending to history:', directory) 74 if g.history.position < g.history.size then 75 directory_movement.clear_history(g.history.position + 1) 76 end 77 78 table.insert(g.history.list, directory) 79 g.history.size = g.history.size + 1 80 g.history.position = g.history.position + 1 81 82 if g.history.size > o.history_size then 83 table.remove(g.history.list, 1) 84 g.history.size = g.history.size - 1 85 end 86 end 87 88 ---@param filepath string 89 function directory_movement.set_current_file(filepath) 90 --if we're in idle mode then we want to open the working directory 91 if filepath == nil then 92 g.current_file.directory = fb_utils.fix_path( mp.get_property("working-directory", ""), true) 93 g.current_file.name = nil 94 g.current_file.path = nil 95 g.current_file.original_path = nil 96 return 97 end 98 99 local absolute_path = fb_utils.absolute_path(filepath) 100 local resolved_path = fb_utils.resolve_directory_mapping(absolute_path) 101 102 g.current_file.directory, g.current_file.name = utils.split_path(resolved_path) 103 g.current_file.original_path = absolute_path 104 g.current_file.path = resolved_path 105 106 if o.cursor_follows_playing_item then cursor.select_playing_item() end 107 ass.update_ass() 108 end 109 110 --the base function for moving to a directory 111 ---@param directory string 112 ---@param nav_type? NavigationType 113 ---@param store_history? boolean default `true` 114 ---@param parse_properties? ParseProperties 115 ---@return thread 116 function directory_movement.goto_directory(directory, nav_type, store_history, parse_properties) 117 local current = g.state.list[g.state.selected] 118 g.state.directory = directory 119 120 if g.state.directory_label then 121 if nav_type == NavType.DOWN then 122 g.state.directory_label = g.state.directory_label..(current.label or current.name) 123 elseif nav_type == NavType.UP then 124 g.state.directory_label = string.match(g.state.directory_label, "^(.-/+)[^/]+/*$") 125 end 126 end 127 128 if o.history_size > 0 and store_history == nil or store_history then 129 directory_movement.append_history(directory) 130 end 131 132 return scanning.rescan(nav_type or NavType.GOTO, nil, parse_properties) 133 end 134 135 ---Move the browser to a particular point in the browser history. 136 ---The history is a linear list of visited directories from oldest to newest. 137 ---If the user changes directories while the current history position is not the head of the list, 138 ---any later directories get cleared and the new directory becomes the new head. 139 ---@param pos number The history index to move to. Clamped to [1,history_length] 140 ---@return number|false # The index actually moved to after clamping. Returns -1 if the index was invalid (can occur if history is empty or disabled) 141 function directory_movement.goto_history(pos) 142 if type(pos) ~= "number" then return false end 143 144 if pos < 1 then pos = 1 145 elseif pos > g.history.size then pos = g.history.size end 146 if not g.history.list[pos] then return false end 147 148 g.history.position = pos 149 directory_movement.goto_directory(g.history.list[pos]) 150 return pos 151 end 152 153 --loads the root list 154 function directory_movement.goto_root() 155 msg.verbose('jumping to root') 156 return directory_movement.goto_directory("") 157 end 158 159 --switches to the directory of the currently playing file 160 function directory_movement.goto_current_dir() 161 msg.verbose('jumping to current directory') 162 return directory_movement.goto_directory(g.current_file.directory) 163 end 164 165 --moves up a directory 166 function directory_movement.up_dir() 167 if g.state.directory == '' then return end 168 169 local cached_parent_dir = g.directory_stack.stack[g.directory_stack.position - 1] 170 if cached_parent_dir then 171 g.directory_stack.position = g.directory_stack.position - 1 172 return directory_movement.goto_directory(cached_parent_dir, NavType.UP) 173 end 174 175 local parent_dir = g.state.directory:match("^(.-/+)[^/]+/*$") or "" 176 177 if o.skip_protocol_schemes and parent_dir:find("^(%a[%w+-.]*)://$") then 178 return directory_movement.goto_root() 179 end 180 181 directory_stack_prepend(parent_dir) 182 return directory_movement.goto_directory(parent_dir, NavType.UP) 183 end 184 185 --moves down a directory 186 function directory_movement.down_dir() 187 local current = g.state.list[g.state.selected] 188 if not current or not fb_utils.parseable_item(current) then return end 189 190 local directory, redirected = fb_utils.get_new_directory(current, g.state.directory) 191 directory_stack_append(directory) 192 return directory_movement.goto_directory(directory, redirected and NavType.REDIRECT or NavType.DOWN) 193 end 194 195 --moves backwards through the directory history 196 function directory_movement.back_history() 197 msg.debug('moving backwards in history to', g.history.list[g.history.position-1]) 198 if g.history.position == 1 then return end 199 directory_movement.goto_history(g.history.position - 1) 200 end 201 202 --moves forward through the history 203 function directory_movement.forwards_history() 204 msg.debug('moving forwards in history to', g.history.list[g.history.position+1]) 205 if g.history.position == g.history.size then return end 206 directory_movement.goto_history(g.history.position + 1) 207 end 208 209 return directory_movement