Skip to content

Commit 5b25e8d

Browse files
switch basic file browsing to use immutable states
Only the most basic operations have been ported, there is still a LOT of broken functionality.
1 parent a7d8a17 commit 5b25e8d

File tree

1 file changed

+114
-61
lines changed

1 file changed

+114
-61
lines changed

file-browser.lua

Lines changed: 114 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,15 @@ function API.coroutine.run(fn, ...)
473473
API.coroutine.resume_err(co, ...)
474474
end
475475

476+
--schedules a coroutine to run when next idle.
477+
--returns the coroutine object
478+
function API.coroutine.schedule(fn, ...)
479+
local co = coroutine.create(fn)
480+
local args = table.pack(...)
481+
mp.add_timeout(0, function() API.coroutine.resume_err(co, table.unpack(args)) end)
482+
return co
483+
end
484+
476485
--get the full path for the current file
477486
function API.get_full_path(item, dir)
478487
if item.path then return item.path end
@@ -951,38 +960,49 @@ end
951960

952961
--disables multiselect
953962
local function disable_select_mode()
954-
state.multiselect_start = nil
955-
state.initial_selection = nil
963+
-- state.multiselect_start = nil
964+
-- state.initial_selection = nil
965+
update_state(2, {
966+
multiselect_start = NIL_STATE,
967+
initial_selection = NIL_STATE
968+
})
969+
print_state()
956970
end
957971

958972
--enables multiselect
959973
local function enable_select_mode()
960-
state.multiselect_start = state.selected
961-
state.initial_selection = API.copy_table(state.selection)
974+
update_state(2, {
975+
multiselect_start = state.selected,
976+
initial_selection = API.copy_table(state.selection)
977+
})
978+
print_state()
962979
end
963980

964981
--calculates what drag behaviour is required for that specific movement
965982
local function drag_select(original_pos, new_pos)
966983
if original_pos == new_pos then return end
967984

985+
local new_selection = API.copy_table(state.selection)
968986
local setting = state.selection[state.multiselect_start]
969987
for i = original_pos, new_pos, (new_pos > original_pos and 1 or -1) do
970988
--if we're moving the cursor away from the starting point then set the selection
971989
--otherwise restore the original selection
972990
if i > state.multiselect_start then
973991
if new_pos > original_pos then
974-
state.selection[i] = setting
992+
new_selection[i] = setting
975993
elseif i ~= new_pos then
976-
state.selection[i] = state.initial_selection[i]
994+
new_selection[i] = state.initial_selection[i]
977995
end
978996
elseif i < state.multiselect_start then
979997
if new_pos < original_pos then
980-
state.selection[i] = setting
998+
new_selection[i] = setting
981999
elseif i ~= new_pos then
982-
state.selection[i] = state.initial_selection[i]
1000+
new_selection[i] = state.initial_selection[i]
9831001
end
9841002
end
9851003
end
1004+
1005+
return new_selection
9861006
end
9871007

9881008
--moves the selector up and down the list by the entered amount
@@ -991,31 +1011,43 @@ local function scroll(n, wrap)
9911011
if num_items == 0 then return end
9921012

9931013
local original_pos = state.selected
1014+
local new_pos
1015+
local new_multiselect
9941016

9951017
if original_pos + n > num_items then
996-
state.selected = wrap and 1 or num_items
1018+
new_pos = wrap and 1 or num_items
9971019
elseif original_pos + n < 1 then
998-
state.selected = wrap and num_items or 1
1020+
new_pos = wrap and num_items or 1
9991021
else
1000-
state.selected = original_pos + n
1022+
new_pos = original_pos + n
1023+
end
1024+
1025+
if state.multiselect_start then
1026+
new_multiselect = drag_select(original_pos, new_pos)
10011027
end
10021028

1003-
if state.multiselect_start then drag_select(original_pos, state.selected) end
1029+
update_state(2, { selected = new_pos, selection = new_multiselect })
10041030
update_ass()
10051031
end
10061032

10071033
--toggles the selection
10081034
local function toggle_selection()
10091035
if not state.list[state.selected] then return end
1010-
state.selection[state.selected] = not state.selection[state.selected] or nil
1036+
-- state.selection[state.selected] = not state.selection[state.selected] or nil
1037+
local selection = API.copy_table(state.selection)
1038+
selection[state.selected] = not selection[state.selected] or nil
1039+
update_state(2, { selection = selection })
10111040
update_ass()
10121041
end
10131042

10141043
--select all items in the list
10151044
local function select_all()
1045+
local selection = {}
10161046
for i,_ in ipairs(state.list) do
1017-
state.selection[i] = true
1047+
selection[i] = true
10181048
end
1049+
1050+
update_state(2, {selection = selection})
10191051
update_ass()
10201052
end
10211053

@@ -1133,18 +1165,18 @@ end
11331165
local function update_list(moving_adjacent)
11341166
msg.verbose('opening directory: ' .. state.directory)
11351167

1136-
state.selected = 1
1137-
state.selection = {}
1168+
-- state.selected = 1
1169+
-- state.selection = {}
11381170

11391171
--loads the current directry from the cache to save loading time
11401172
--there will be a way to forcibly reload the current directory at some point
11411173
--the cache is in the form of a stack, items are taken off the stack when the dir moves up
1142-
if cache[1] and cache[#cache].directory == state.directory then
1143-
msg.verbose('found directory in cache')
1144-
cache:apply()
1145-
state.prev_directory = state.directory
1146-
return
1147-
end
1174+
-- if cache[1] and cache[#cache].directory == state.directory then
1175+
-- msg.verbose('found directory in cache')
1176+
-- cache:apply()
1177+
-- state.prev_directory = state.directory
1178+
-- return
1179+
-- end
11481180
local directory = state.directory
11491181
local list, opts = parse_directory(state.directory, { source = "browser" })
11501182

@@ -1157,19 +1189,21 @@ local function update_list(moving_adjacent)
11571189
end
11581190

11591191
--apply fallbacks if the scan failed
1160-
if not list and cache[1] then
1161-
--switches settings back to the previously opened directory
1162-
--to the user it will be like the directory never changed
1163-
msg.warn("could not read directory", state.directory)
1164-
cache:apply()
1165-
return
1166-
elseif not list then
1192+
-- if not list and cache[1] then
1193+
-- --switches settings back to the previously opened directory
1194+
-- --to the user it will be like the directory never changed
1195+
-- msg.warn("could not read directory", state.directory)
1196+
-- cache:apply()
1197+
-- return
1198+
if not list then
11671199
msg.warn("could not read directory", state.directory)
11681200
list, opts = root_parser:parse()
11691201
end
11701202

1171-
state.list = list
1172-
state.parser = opts.parser
1203+
local finished_state = {}
1204+
1205+
finished_state.list = list
1206+
finished_state.parser = opts.parser
11731207

11741208
--this only matters when displaying the list on the screen, so it doesn't need to be in the scan function
11751209
if not opts.escaped then
@@ -1179,54 +1213,71 @@ local function update_list(moving_adjacent)
11791213
end
11801214

11811215
--setting custom options from parsers
1182-
state.directory_label = opts.directory_label
1183-
state.empty_text = opts.empty_text or state.empty_text
1216+
finished_state.directory_label = opts.directory_label
1217+
finished_state.empty_text = opts.empty_text
11841218

11851219
--we assume that directory is only changed when redirecting to a different location
11861220
--therefore, the cache should be wiped
11871221
if opts.directory then
1188-
state.directory = opts.directory
1222+
finished_state.directory = opts.directory
11891223
cache:clear()
11901224
end
11911225

11921226
if opts.selected_index then
1193-
state.selected = opts.selected_index or state.selected
1194-
if state.selected > #state.list then state.selected = #state.list
1195-
elseif state.selected < 1 then state.selected = 1 end
1227+
finished_state.selected = opts.selected_index or state.selected
1228+
if finished_state.selected > #list then finished_state.selected = #list
1229+
elseif finished_state.selected < 1 then finished_state.selected = 1 end
11961230
end
11971231

11981232
if moving_adjacent then select_prev_directory()
11991233
else select_playing_item() end
1200-
state.prev_directory = state.directory
1234+
finished_state.prev_directory = finished_state.directory
1235+
1236+
print(utils.to_string(finished_state))
1237+
return finished_state
12011238
end
12021239

12031240
--rescans the folder and updates the list
1204-
local function update(moving_adjacent)
1241+
local function update(new_state, moving_adjacent)
1242+
if not new_state then new_state = {} end
1243+
local directory = new_state.directory or state.directory
1244+
cache:clear()
1245+
12051246
--we can only make assumptions about the directory label when moving from adjacent directories
1206-
if not moving_adjacent then
1207-
state.directory_label = nil
1208-
cache:clear()
1209-
end
1247+
-- if not moving_adjacent then
1248+
-- state.directory_label = nil
1249+
-- cache:clear()
1250+
-- end
12101251

1211-
state.empty_text = "~"
1212-
state.list = {}
1213-
disable_select_mode()
1214-
update_ass()
1252+
new_state.directory = directory
1253+
new_state.empty_text = "~"
1254+
new_state.list = {}
1255+
1256+
-- set_state(1, new_state)
1257+
-- -- disable_select_mode()
1258+
-- update_ass()
12151259

12161260
--the directory is always handled within a coroutine to allow addons to
12171261
--pause execution for asynchronous operations
1218-
API.coroutine.run(function()
1219-
state.co = coroutine.running()
1220-
update_list(moving_adjacent)
1221-
state.empty_text = "empty directory"
1262+
new_state.co = API.coroutine.schedule(function()
1263+
local newer_state = update_list(moving_adjacent)
1264+
if not newer_state then error() end
1265+
newer_state.empty_text = newer_state.empty_text or NIL_STATE
1266+
update_state(1, newer_state)
1267+
print_state()
1268+
12221269
update_ass()
12231270
end)
1271+
1272+
set_state(1, new_state)
1273+
print_state()
1274+
update_ass()
12241275
end
12251276

12261277
--the base function for moving to a directory
12271278
local function goto_directory(directory)
1228-
state.directory = directory
1229-
update(false)
1279+
-- state.directory = directory
1280+
update({ directory = directory }, false)
12301281
end
12311282

12321283
--loads the root list
@@ -1251,13 +1302,14 @@ local function up_dir()
12511302
index = dir:find("[/\\]")
12521303
end
12531304

1254-
if index == nil then state.directory = ""
1255-
else state.directory = dir:sub(index):reverse() end
1305+
if index == nil then dir = ""
1306+
else dir = dir:sub(index):reverse() end
12561307

12571308
--we can make some assumptions about the next directory label when moving up or down
1258-
if state.directory_label then state.directory_label = state.directory_label:match("^(.+/)[^/]+/$") end
1309+
local dir_label = nil
1310+
if state.directory_label then dir_label = state.directory_label:match("^(.+/)[^/]+/$") end
12591311

1260-
update(true)
1312+
update({ directory = dir, directory_label = dir_label } ,true)
12611313
cache:pop()
12621314
end
12631315

@@ -1268,11 +1320,11 @@ local function down_dir()
12681320

12691321
cache:push()
12701322
local directory, redirected = API.get_new_directory(current, state.directory)
1271-
state.directory = directory
12721323

12731324
--we can make some assumptions about the next directory label when moving up or down
1274-
if state.directory_label then state.directory_label = state.directory_label..(current.label or current.name) end
1275-
update(not redirected)
1325+
local dir_label
1326+
if state.directory_label then dir_label= state.directory_label..(current.label or current.name) end
1327+
update({ directory = directory, directory_label = dir_label }, not redirected)
12761328
end
12771329

12781330

@@ -1330,7 +1382,7 @@ local function escape()
13301382
--if multiple items are selection cancel the
13311383
--selection instead of closing the browser
13321384
if next(state.selection) or state.multiselect_start then
1333-
state.selection = {}
1385+
update_state(2, { selection = {} })
13341386
disable_select_mode()
13351387
update_ass()
13361388
return
@@ -2247,6 +2299,7 @@ local function setup_root()
22472299
end
22482300
end
22492301

2302+
set_state(0, state)
22502303
setup_root()
22512304

22522305
setup_parser(file_parser, "file-browser.lua")

0 commit comments

Comments
 (0)