telescope.nvim icon indicating copy to clipboard operation
telescope.nvim copied to clipboard

Return value(s) from picker

Open smackesey opened this issue 4 years ago • 10 comments

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.

smackesey avatar Aug 22 '21 22:08 smackesey

+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.

kkharji avatar Aug 23 '21 00:08 kkharji

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.

tjdevries avatar Aug 23 '21 12:08 tjdevries

(not implemented)

tjdevries avatar Aug 23 '21 12:08 tjdevries

More relevant discussion can also be found here https://github.com/neovim/neovim/pull/15202

fdschmidt93 avatar Aug 23 '21 13:08 fdschmidt93

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 ...

smackesey avatar Aug 23 '21 19:08 smackesey

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

p00f avatar Nov 19 '21 15:11 p00f

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 avatar Nov 19 '21 16:11 fdschmidt93

@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?

p00f avatar Nov 19 '21 16:11 p00f

For telescope? I'm afraid not.

fdschmidt93 avatar Nov 19 '21 16:11 fdschmidt93

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).

chancez avatar Dec 02 '25 18:12 chancez

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.

chancez avatar Dec 16 '25 19:12 chancez