telescope.nvim
telescope.nvim copied to clipboard
Allow users to "resume" last known picker state
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_filesmapped to<leader>f. - I hit
<leader>fto 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>fand 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 resumerequires another keybinding.- I have
<leader>fin 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/
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?
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.
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,
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)
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)