-- by strlst
-- https://notabug.org/strlst/vis-dmenu-tools
-- SPDX-License-Identifier: AGPL-3.0-or-later

-- makes use of established utilities such as find and dmenu to offer advanced
-- file picking or browsing capability

-- perhaps more to come?

-- '<++>' serve as placeholders for paths
menu = 'dmenu -b -l 8 -p open'
commands = {
	-- 1: command used get containing directory
	'dirname $(readlink -f <++>)',
	-- 2: command used to list files recursively
	'find <++> -type f -not -path \'*/\\.*\' | '..menu,
	-- 3: command used to list files NON-recursively
	'find <++> -maxdepth 1 -type f -not -path \'*/\\.*\' | '..menu,
	-- 4: command used to list directories recursively
	'(echo .. && find <++> -type d -not -path \'*/\\.*\') | sort | '..menu,
	-- 5: command used to list files NON-recursively
	'(echo .. && find <++> -maxdepth 1 -type d -not -path \'*/\\.*\') | sort | '..menu
}

-- helper functions

function file_contained_in(file, range)
	local path = file.path
	
	if not path then
		vis:info('error: file not yet named')
		return nil
	end

	local retcode, stdout, stderr = vis:pipe(
		file,
		range,
		commands[1]:gsub('<%+%+>', path)
	)

	if not retcode == 0 then
		vis:info('error: reading directory file is contained in failed ('..retcode..')')
		return nil
	end

	return stdout
end

function arg_check(func_name, argv)
	if not (#argv > 1) then
		vis:info('error: '..func_name..' requires more arguments')
		return false
	end
	return true
end

-- commands

vis:command_register('dmenu-pick', function(argv, force, win, selection, range)
	if not arg_check('dmenu-pick', argv) then
		return false
	end

	local file = win.file

	local retcode, stdout, stderr = vis:pipe(
		file,
		range,
		-- 'find '..argv[1]..' -type f -not -path \'*/\\.*\' | dmenu -b -l 8 -p open'
		commands[tonumber(argv[1])]:gsub('<%+%+>', argv[2])
	)

	if stdout == nil then
		return false
	end

	vis:command('open '..stdout)
end, 'Pick a filename contained recursively within a directory using dmenu')

vis:command_register('dmenu-pick-current', function(argv, force, win, selection, range)
	local dir = file_contained_in(win.file, range)

	if dir == nil then
		return false
	end

	vis:command('dmenu-pick 2 '..dir)
end, 'Pick a filename contained recursively within a directory using dmenu')

vis:command_register('dmenu-browse', function(argv, force, win, selection, range)
	if not arg_check('dmenu-browse', argv) then
		return false
	end

	local file = win.file

	local retcode, stdout, stderr = vis:pipe(
		file,
		range,
		-- '(echo .. && find '..argv[1]..' -type d -not -path \'*/\\.*\') | sort | dmenu -b -l 8 -p open'
		-- argv[1]
		commands[tonumber(argv[1])]:gsub('<%+%+>', argv[2])
	)

	if stdout == nil then
		return false
	end

	vis:command('dmenu-pick 3 '..stdout)
end, 'Pick a filename contained recursively within a directory using dmenu')

vis:command_register('dmenu-browse-current', function(argv, force, win, selection, range)
	local dir = file_contained_in(win.file, range)

	if dir == nil then
		return false
	end

	vis:command('dmenu-browse 4 '..dir)
end, 'Pick a filename contained recursively within a directory using dmenu')

-- motions for commands

vis:map(vis.modes.NORMAL, "<C-o>", function()
	vis:command('dmenu-pick-current')
end, "execute dmenu-pick for the directory containing the currently opened file")

vis:map(vis.modes.NORMAL, "<C-p>", function()
	vis:command('dmenu-browse-current')
end, "execute dmenu-pick for the directory containing the currently opened file")
