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