Return value(s) from picker
Sometimes it is useful to use a picker to provide user input at an intermediate step in a longer script/routine. However, the current API does not support this well (that I can figure out-- maybe I'm wrong). Specifically, Picker:find(), which is the final call for all builtin picker functions I've looked at (e.g. find_files), doesn't return anything. Ideally it would be able to return one or more selected entries. For example:
print('Choose one or more files:')
files = require('telescope').builtin.find_files()
-- ... do something with files ...
My current workaround to achieve this is to store the current selection inside a global in a custom action, and read that global after the Picker:find() call terminates.
It would be nice if Picker:find() returned a value, and a builtin action was provided to set this value and exit the main loop so it is returned.
+1: it beats calling the function from within the picker. It might be tricky todo and might require the users to learn async :D because I don't think find_files blocks anything. Your function will finish executing by the time you see the picker.
yes, as written, this is not possible (you are in the middle of executing random lua code, and then I want to execute random lua code that can stop, schedule other code, wait for other things, etc.)
It would be possible to do something like this:
async.void(function()
local results = require('telescope.async')(require('telescope.builtin').find_files, opts)
print(files)
end)
or something like that.
(not implemented)
More relevant discussion can also be found here https://github.com/neovim/neovim/pull/15202
I see that the async nature of Telescope complicates this request a lot.
What about a generic selection_callback option that's called either (a) when a picker closes; or (b) in a builtin process_selection action (that closes the picker and passes the selection to the callback)? Then you could do something like this:
print('Choose one or more files:')
local files
require('telescope').builtin.find_files{
selection_callback = function (entries) files = entries end,
mappings = {
i = { ['<CR>'] = require('telescope.actions').process_selection },
}
}
-- ... do something with files ...
My current workaround to achieve this is to store the current selection inside a global in a custom action, and read that global after the Picker:find() call terminates.
@smackesey how do you do it?
local val = nil
pickers.new(nil, {
prompt_title = "",
-- ....
attach_mappings = function(prompt_bufnr, _map)
actions.select_default:replace(function()
actions.close(prompt_bufnr)
local selection = actions_state.get_selected_entry()
val = selection.value
end)
end,
}):find()
print(val) -- still nil
val is still nil at the print statement
The canonical way would be something akin to vim.ui.select ala https://github.com/nvim-telescope/telescope-ui-select.nvim (specifically, see here).
@fdschmidt93 I want to avoid callbacks because there is a lot of other context from nested calls for what I'm trying to do. Is there another solution like blocking until the user makes a choice?
For telescope? I'm afraid not.
This would be really useful as I have a few BufEnter autocmds which run telescope pickers, and if you exit the picker without a selection, there's no way easy way to detect that easily, so the autocmd just re-runs the picker until you pick something (and the buffer changes).
In case it helps anyone else: I have had mild success in at least getting results from find_files by taking the same approach as to_fuzzy_refine.
For example, I wanted to "fuzzy refine" the results of find_files using live_grep. I did it like so:
-- Refine current find_files results via live_grep
local function find_files_to_live_grep(prompt_bufnr)
local current_picker = action_state.get_current_picker(prompt_bufnr)
if not current_picker.prompt_title:find('Find Files') then
print("Can only refine find_files to live_grep")
return
end
-- Grab the current search results
local results = {}
for entry in current_picker.manager:iter() do
table.insert(results, entry.path)
end
-- Close the current picker
actions.close(prompt_bufnr)
-- Open live_grep with the current results as the new source
builtin.live_grep({
prompt_title = current_picker.prompt_title .. ' (refine live_grep)',
-- search_dirs also takes file names, it's a misnomer.
search_dirs = results,
})
end
Then I have it as a mapping:
pickers = {
find_files = {
map('i', '<C-f>', find_files_to_live_grep)
return true
end,
},
Hope this helps anyone else trying to do something similar. It unfortunately, does not work for my previous use-case, as this only works when the picker is currently open, so I can't check the results after the picker is closed.