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

Allow users to "resume" last known picker state

Open strange opened this issue 3 years ago • 5 comments

Is your feature request related to a problem? Please describe.

  • I want to find and poke around in all files that include the text "foobar".
  • I have :Telescope list_files mapped to <leader>f.
  • I hit <leader>f to open the modal and filter the results by entering the text "foobar".
  • I enter the first file to look around.
  • I now want to go to the next file. I hit <leader>f and have to enter "foobar" again to list the files.

Describe the solution you'd like

It would be neat if I could repeatedly hit <leader>f and the picker would resume to the last known state.

Describe alternatives you've considered

Reddit pointed me towards :Telescope resume which, in large, solves my problem. The solution has the following drawbacks:

  • :Telescope resume requires another keybinding.
  • I have <leader>f in muscle memory, if I hit it by accident the last known state is reset.
  • If I use a different picker in-between navigation between files, the state is reset.

Additional context

This is applicable to other pickers as well (most prominently zk-nvim in my case).

The feature would probably require a more advanced cache eviction strategy, updated documentation, a carefully thought-out API to reduce unnecessary memory usage (as opposed to blindly caching all pickers) etc. I could get used to a separate binding to resume the state. But let's discuss!

Reddit discussion here: https://www.reddit.com/r/neovim/comments/s696vk/telescope_fzf_ag_for_live_grep/ht5j6it/

strange avatar Jan 19 '22 08:01 strange

As mentioned on Reddit, I'd be happy to accept a PR that essentially allows something like the following:

local actions = require "telescope.actions"
local state = require "telescope.state"

local last_find_files = nil

local function find_files(opts)
  opts = opts or {}
  if last_find_files == nil then
    require "telescope.builtin".find_files {
      attach_mappings = function(prompt_bufnr, map)
        actions.close:enhance {
          post = function()
            -- taken from builtin.resume maybe rfc into a `telescope.utils`.get_last_picker
            local cached_pickers = state.get_global_key "cached_pickers"
            if cached_pickers == nil or vim.tbl_isempty(cached_pickers) then
              print "No picker(s) cached"
              return
            end
            last_find_files = cached_pickers[1]  -- last picker is always 1st
          end
        }    
        return true
      end
    } 
  else
    require "telescope.builtin".resume { picker = last_find_files }
  end
end

which I think is a reasonable interface to enable such use cases. builtin.resume and builtin.pickers are the generic solutions we should have in core. Otherwise, it is not perfectly clear what the ideal solution would be that tailors to everyone's needs.

I haven't tried the code, but to that end, I think we'd primarily need a PR that changes builtin.resume (and maybe builtin.pickers) to accept a picker(s) directly, which seems like a pretty straightforward PR :)

Would you be interested on working on that?

fdschmidt93 avatar Jan 19 '22 09:01 fdschmidt93

A more generalized version of this could be being able to cycle through the past K searches. I often need to grep through files to find/fix something and being able to hit up/down to move through my previous searches would be incredible.

pmdaly avatar Jan 25 '22 14:01 pmdaly

https://github.com/nvim-telescope/telescope-smart-history.nvim

This should be exactly what you are describing. Setup as per README and then map in require "telescope".setup { defaults = { mappings = { ... } } }

        ["<C-Down>"] = actions.cycle_history_next,
        ["<C-Up>"] = actions.cycle_history_prev,

fdschmidt93 avatar Jan 25 '22 14:01 fdschmidt93

Interesting. I'll try to get some time next week to take a look at everything.

Only thing is that I've gotten quite used to using :Telescope resume since creating this issue :)

(what I'm currently looking into now is a way to refresh the result list when I resume a state ... like if I use list_files to search for files matching foo, enter a file and delete the occurrence, and resume I'd like to be able to update the result)

strange avatar Jan 26 '22 08:01 strange

Here's a cute solution that I rigged up to make various commands automatically detect that I am restarting the last search and resume it with builtins.resume.

_last_picker = nil
_last_ctx = nil
local function telescope_middleware(func, ctxfunc)
  function inner()
    if ctxfunc == nil then
      ctx = nil
    else
      ctx = ctxfunc()
    end
    if func == _last_picker and vim.deep_equal(ctx, _last_ctx) then
      builtin.resume()
    else
      _last_picker = func
      _last_ctx = ctx
      func()
    end
  end
  return inner
end

As an example context function,

-- a function that uses the current word under the cursor as a proxy for "what the lsp is going to search for when you ask for usages"
local function tokenctx()
  cursor0 = vim.api.nvim_win_get_cursor(0)
  vim.cmd.normal("lb")
  cursor = vim.api.nvim_win_get_cursor(0)
  word = vim.call('expand','<cword>')
  vim.api.nvim_win_set_cursor(0, cursor0)
  return {cursor, word}
end

And as an example usage:

vim.keymap.set('n', 'gu', telescope_middleware(builtin.lsp_references, tokenctx), bufopts)
vim.keymap.set('n', 'gn', telescope_middleware(builtin.find_files), nil)

rhelmot avatar Nov 19 '23 22:11 rhelmot