telescope.nvim
telescope.nvim copied to clipboard
find_files and grep in selected directory
Is your feature request related to a problem? Please describe.
the builtin grep
and find_files
searches the current directory. in cases when you're working in a monorepo or a project with multiple apps, it may be harder to narrow down the searches with the builtin grep and find_files. are there plans to support these functions natively?
Describe the solution you'd like
i have a working library which can do this (dir-telescope.nvim). i just wanted to know if there are future plans to directly implement this in telescope.nvim, thanks
You can achieve this with telescope-file-browser and a custom function.
This action will launch telescope-file-browser in folder mode, and then launch live_grep within the selected directory:
local ts_select_dir_for_grep = function(prompt_bufnr)
local action_state = require("telescope.actions.state")
local fb = require("telescope").extensions.file_browser
local live_grep = require("telescope.builtin").live_grep
local current_line = action_state.get_current_line()
fb.file_browser({
files = false,
depth = false,
attach_mappings = function(prompt_bufnr)
require("telescope.actions").select_default:replace(function()
local entry_path = action_state.get_selected_entry().Path
local dir = entry_path:is_dir() and entry_path or entry_path:parent()
local relative = dir:make_relative(vim.fn.getcwd())
local absolute = dir:absolute()
live_grep({
results_title = relative .. "/",
cwd = absolute,
default_text = current_line,
})
end)
return true
end,
})
end
The action also captures the current line from telescope, because it's meant to be launched from within live_grep:
telescope.setup({
pickers = {
live_grep = {
mappings = {
i = {
["<C-f>"] = ts_select_dir_for_grep,
},
n = {
["<C-f>"] = ts_select_dir_for_grep,
},
},
},
},
})
So the flow is:
- Launch live_grep
- Start typing
- Realize that you need to scope to directory
-
<Ctrl-f>
- Select directory
- Get returned to live_grep, scoped to the directory you selected and with your search args still populated
You can repeat 3-6 as many times as needed.
(If you use live_grep_args, replace require("telescope.builtin").live_grep
with require("telescope").extension.live_grep_args.live_grep
)
The above solution is of course much nicer, but one can simply specify the search_dirs
to limit the scope of the search.
:Telescope find_files search_dirs={"/path/to/relevant/folder/"}
What would be even nicer than spending time figuring out how to make this work and then writing all the above code that uses an extension would be if this functionality was builtin.
I find myself constantly running into this, especially in monorepos. With all 3 of my most used pickers: file picker, text search and live grep. I would also much prefer to have this feature natively instead of adding more plugin and script bloat. Telescope already does almost everything I personally need from it and hope it could extend to this as well at some point. It's an extremely useful utility for me.
Example scenario:
- Work in
/something/service-a/src
- Remember you did this "thing" in an another app
- Launch
- Look for "thing"
- Notice you cannot look into
/something/service-b/src
- Open split terminal
-
$ cd ../../service-b/src
-
$ rg "thing"
- ...
I would prefer this workflow:
- Work in
/something/service-a/src
- Remember you did this "thing" in an another app
- Launch
- Swap to "change active directory" picker momentarily:
<some-mapping>
- Browse to correct directory
- Look for "thing"
- ...
I imagine directory picker would be a normal file browser with traversal capabilities (<CR>
/ <C-CR>
|<BS>
to traverse) that shows directories on top and files inside the currently viewed directory under those with some mapping changing the current working directory to the one under the cursor and swapping back to previous picker (<some-mapping>
again), possibly an another mapping for changing the working directory for all future pickers as well (<SOME-MAPPING>
maybe?).
../
service-a/
service-b/
foo.txt
bar.sh
One can dream but perhaps someone well versed with the plugin might get a spark from this idea. Thanks for all you've done already, love the plugin!
For grep
in monorepo, I have replaced live_grep
with live_grep_args
. Do we have a similar thing for find_files
for example find_files_args
? I know about the search_dirs
option but it is not very convenient to use.
The dir-telescope plugin linked in the OP is what I'd recommend. It should really be builtin to telescope.
If you use telescope-file-browser you can also use an custom action like below from within a directory to live_grep
/find_files
for things only in that directory.
local Path = require("plenary.path")
local action_state = require("telescope.actions.state")
local actions = require("telescope.actions")
local open_using = function(finder)
return function(prompt_bufnr)
local current_finder = action_state.get_current_picker(prompt_bufnr).finder
local entry = action_state.get_selected_entry()
local entry_path
if entry.ordinal == ".." then
entry_path = Path:new(current_finder.path)
else
entry_path = action_state.get_selected_entry().Path
end
local path = entry_path:is_dir() and entry_path:absolute() or entry_path:parent():absolute()
actions.close(prompt_bufnr)
finder({ cwd = path })
end
end
-- map keys to for file_browser to...
open_using(builtin.find_files),
open_using(builtin.live_grep),
I tried some of the solutions mentioned above today. I like the idea of scoping the search to a directory usingtelescope-file-browser
but it only allows me to scope within the current directory. Is there a way to increase the scope of search? For example if I want to add the parent directory to the scope of search as well (monorepo use case as mentioned above by puttehi also). That means if I am inside package-a
and I want to search something in package-b
or in the entire packages
folder.
../
packages
package-a/
package.json
package-b/
package.json
package.json
Thanks for the help, much appreciated :)
dir-telescope plugin linked in the OP allows you to make multiple selections and you can then search through all those directories at the same time, is that what you mean?
../
packages
package-a/
package.json
package-b/
package.json
package.json
Hi Brian. Actually, both the plugins were working similarly for me. Suppose I take the above folder structure as an example. If my current buffer is packages/package-a/package.json
and I do the FileInDirectory
, it cannot find packages/package-b/package.json
. It searches in the scope of packages/package-a/
. I wanted to search in the scope of packages/
when my current buffer is packages/package-a/package.json
.
I'll try to add a reproducible demo link here and probably I should also ask this query in the dir telescope
repo. Sorry, I'm new to vim/neovim and it is also possible that I have made a blunder :)
Are you using LazyVim? It sounds like you are running search in root dir
(which is the package/project dir) instead of cwd
(which is where you opened up Neovim (at the root of the monorepo I'm guessing).
https://www.lazyvim.org/keymaps
I am using Neovim without LazyVim. And yes. I open Neovim at the root of the monorepo. From the root level I can grep/find_file everything but when I open a file inside any package, grep/find_file only works in the scope of that package.
I think something similar is happening to you, the root dir
is being passed in somewhere and that's why your searches are being rooted there. No idea how it's happening you'll have to dig into your config to figure it out.
Finally found the issue in my config. I was using project.nvim
plugin which integrates with telescope
. In the config of that plugin, I was using package.json
as a pattern to detect the project's root. Since in a JS mono repo, all the packages have their own package.json
, the live_grep
/find_files
was getting rooted to the current package.
I have the same issue as @pr7prashant : I would sometimes like to search outside of the current root. One specific use case that I'm running into right now is that as I'm setting up my configuration, I frequently want to search for where certain mappings/plugins are set up by grepping inside the plugin folder, e.g. ~/.local/nvim/share/lazy
. I am using LazyVim which has options for cwd
and root dir
as @briandipalma said, but this is folder will pretty much always lie outside the scope of these directories.
I tried passing in an option to the dir-telescope.nvim
plugin, but it doesn't appear to support arguments. Any ideas for how to accomplish this?
@jamestrew how does your solution differ from the one above by schoblaska, which both use telescope-file-browser
?
You could take a look at LazyVim's keybindings and add your own specifying that one directory as the cwd
?
I want to be able to live_grep some string and then, while I have results, further filter the results to only those in a specific subdirectory. Is that not possible?
@thallada not exactly what you're asking for but you can <C-space>
to refine the search with fuzzy matching from within the live_grep picker
@schoblaska I know it's been a while, but I am trying to use your suggested solution. I used it with normal live_grep
without issues, but I want to switch to live_grep_args
. So I updated require("telescope.builtin").live_grep
with require("telescope").extension.live_grep_args.live_grep
and added a mapping:
extensions = {
live_grep_args = {
mappings = {
i = {
["<C-p>"] = ts_select_dir_for_grep,
},
},
},
}
But when I try to invoke the filebrowser I get this error:
E5108: Error executing lua: .../.config/nvim/lua/plugins/nvim-telescope.lua:16: attempt to index field 'extension' (a nil value)
stack traceback:
.../.config/nvim/lua/plugins/nvim-telescope.lua:16: in function 'key_func'
...k/packer/start/telescope.nvim/lua/telescope/mappings.lua:257: in function <...k/packer/start/telescope.nvim/lua/telescope/mappings.lua:256>
Where line 16 is line 5 in the function:
local ts_select_dir_for_grep = function(_)
local action_state = require("telescope.actions.state")
local current_line = action_state.get_current_line()
local fb = require("telescope").extensions.file_browser
local live_grep = require("telescope").extension.live_grep_args.live_grep -- this is line 16
fb.file_browser({
files = false,
depth = false,
attach_mappings = function(_)
require("telescope.actions").select_default:replace(function()
local entry_path = action_state.get_selected_entry().Path
local dir = entry_path:is_dir() and entry_path or entry_path:parent()
local relative = dir:make_relative(vim.fn.getcwd())
live_grep({
cwd = dir:absolute(),
default_text = current_line,
results_title = relative .. "/",
})
end)
return true
end,
})
end
For all other usecases the filebrowser works as expected, so any pointer on what I missed? Thanks!!
EDIT: Also tried it with require("telescope").extension.live_grep_args.live_grep_args
but gives the same result
@svanharmelen
attempt to index field 'extension' (a nil value)
The error says that require("telescope").extension
is nil
. It should be require("telescope").extensions
(with an "s").
I also use live_grep_args
. Here's the function I use:
select_dir_for_grep = function(prompt_bufnr)
local action_state = require("telescope.actions.state")
local fb = require("telescope").extensions.file_browser
local lga = require("telescope").extensions.live_grep_args
local current_line = action_state.get_current_line()
fb.file_browser({
files = false,
depth = false,
attach_mappings = function(prompt_bufnr)
require("telescope.actions").select_default:replace(function()
local entry_path = action_state.get_selected_entry().Path
local dir = entry_path:is_dir() and entry_path or entry_path:parent()
local relative = dir:make_relative(vim.fn.getcwd())
local absolute = dir:absolute()
lga.live_grep_args({
results_title = relative .. "/",
cwd = absolute,
default_text = current_line,
})
end)
return true
end,
})
end
Ahh... 🙈 Thanks for your quick response @schoblaska! Works like a charm now 👍🏻
So I'm pretty sure everything worked as expected 3 days ago, but now I'm having an issue again. If I open fb directly it works fine:
But if I open fb from the live_grep window it shows me this:
So the two test dirs are now in the preview windows instead of in the picker window and I have nothing to select from in the picker window. Trying to type doesn't change anything, so I'm not able to select any directory now.
I just now copied the exact function you pasted 3 days ago, but that gives me the same result. Any idea what may cause this behavior? Did anything change (I did update all my plugins)?
For completeness, the rest of my Telescope config looks like this:
@svanharmelen Does folder browser (:Telescope file_browser files=false
) work if you open it by itself? If not, it might be an issue with telescope-file-browser.
Thanks again for your quick response @schoblaska! I just reverted the last commit from 3 days ago, and that fixed the problem. So its indeed a bug with the file-browser plugin 😏 Will open an issue there to see if they can get it sorted.
@thallada not exactly what you're asking for but you can
<C-space>
to refine the search with fuzzy matching from within the live_grep picker
@jamestrew are you sure this is correct? I don't see this binding in the telescope default bindings.
@dudicoco It's here: https://github.com/nvim-telescope/telescope.nvim/blob/b744cf59752aaa01561afb4223006de26f3836fd/lua/telescope/builtin/__files.lua#L180
thanks @briandipalma. this binding feels like the best solution to the issue.
VSC*ode search has include/exclude dir. Would be nice to be able to do something like this with Telescope.
@gitkumi Have you tried the refine search feature triggered with C-space
and installing https://github.com/nvim-telescope/telescope-fzf-native.nvim ? I find by using !
to remove what I don't need and refining with more terms (for example using ^
) I get quite close to what I need.
So I'd start searching for my-search-term
, then I press C-space
and the term is "consumed" by the picker (it becomes the Find word (my-search-term)
you see below), so the input is empty. I can then filter out files like test files by writing !test
and lets say less files with !.less
and filter in a directory by typing ^from/root/my/directory
so I'd type !test !.less ^from/root/my/directory
in the input and I get what this issue is requesting. Does that make sense? Just add a space between the different search terms.
Frankly I think this issue can be closed as I think this is what people are looking for it just needed better documentation.
I wanted to test how this works, so added the fzf-native
plugin but <C-space>
doesn't seem to do anything. Is there something I should enable/disable/configure for that to work? Tried it with find_files
, live_grep
and file_browser
, all the same result (nothing happens).
Thanks!