Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

find_files and grep in selected directory #2201

Open
princejoogie opened this issue Oct 19, 2022 · 36 comments
Open

find_files and grep in selected directory #2201

princejoogie opened this issue Oct 19, 2022 · 36 comments
Labels
enhancement Enhancement to performance, inner workings or existent features

Comments

@princejoogie
Copy link

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

@princejoogie princejoogie added the enhancement Enhancement to performance, inner workings or existent features label Oct 19, 2022
@princejoogie princejoogie changed the title find and grep in selected directory find_files and grep in selected directory Oct 19, 2022
@schoblaska
Copy link

schoblaska commented Oct 19, 2022

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:

  1. Launch live_grep
  2. Start typing
  3. Realize that you need to scope to directory
  4. <Ctrl-f>
  5. Select directory
  6. Get returned to live_grep, scoped to the directory you selected and with your search args still populated

asciicast

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)

@sotte
Copy link

sotte commented Nov 6, 2022

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/"}

@briandipalma
Copy link
Contributor

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.

@puttehi
Copy link

puttehi commented Mar 14, 2023

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!

@pr7prashant
Copy link

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.

@briandipalma
Copy link
Contributor

The dir-telescope plugin linked in the OP is what I'd recommend. It should really be builtin to telescope.

@jamestrew
Copy link
Contributor

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

@pr7prashant
Copy link

pr7prashant commented Apr 2, 2023

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

@briandipalma
Copy link
Contributor

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?

@pr7prashant
Copy link

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

@briandipalma
Copy link
Contributor

briandipalma commented Apr 4, 2023

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

@pr7prashant
Copy link

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.

@briandipalma
Copy link
Contributor

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.

@pr7prashant
Copy link

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.

@albertfgu
Copy link

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?

@briandipalma
Copy link
Contributor

You could take a look at LazyVim's keybindings and add your own specifying that one directory as the cwd?

@thallada
Copy link

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?

@jamestrew
Copy link
Contributor

@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

@svanharmelen
Copy link

svanharmelen commented Jul 27, 2023

@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

@schoblaska
Copy link

schoblaska commented Jul 27, 2023

@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

@svanharmelen
Copy link

Ahh... 🙈 Thanks for your quick response @schoblaska! Works like a charm now 👍🏻

@svanharmelen
Copy link

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:

image

But if I open fb from the live_grep window it shows me this:

image

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:

image

@schoblaska
Copy link

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

@svanharmelen
Copy link

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.

@dudicoco
Copy link

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

@briandipalma
Copy link
Contributor

@dudicoco It's here:

map("i", "<c-space>", actions.to_fuzzy_refine)

@dudicoco
Copy link

thanks @briandipalma.
this binding feels like the best solution to the issue.

@gitkumi
Copy link

gitkumi commented Mar 15, 2024

VSC*ode search has include/exclude dir. Would be nice to be able to do something like this with Telescope.

1

@briandipalma
Copy link
Contributor

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

image

Frankly I think this issue can be closed as I think this is what people are looking for it just needed better documentation.

@svanharmelen
Copy link

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!

@dudicoco
Copy link

@svanharmelen perhaps C-space is already used by your OS.

Try rebinding it:

		config = function()
			require("telescope").setup({
				defaults = {
					mappings = {
						i = {
							["<C-f>"] = require("telescope.actions").to_fuzzy_refine,
						},
					},
				},
			})
			require("telescope").load_extension("fzf")
		end

@gitkumi
Copy link

gitkumi commented Mar 19, 2024

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

image

Frankly I think this issue can be closed as I think this is what people are looking for it just needed better documentation.

Thank you for the detailed explanation, I'll try this and see how it goes.

@kzlouge
Copy link

kzlouge commented Apr 16, 2024

I use this in my neovim keybindings:
{ "<leader>ff", ":Telescope find_files cwd=", desc = "Find Files In", },
Use : instead of <cmd> to avoid no <cr> error.
Add the dir you want to search in the command line.

@swoh816
Copy link

swoh816 commented Apr 16, 2024

What I think would be nice is to have a functionality to toggle between live_grep/file_find and telescope-file-browser. I have exactly the same workflow as what @albertfgu described in #2201 (comment) where I need to work outside both CWD and root directory. You can achieve that by doing search_dirs = { desired_directory } as mentioned by @sotte in #2201 (comment), which is quite cumbersome to type in command mode (as mentioned by @pr7prashant in #2201 (comment)).

If anyone knows that either Telescope or telescope-file-browser already offers such functionality, please do share, I'm sure it's going to save many people in the current issue. Since I don't know such functionality exists, I created an issue in telescope-file-browser: nvim-telescope/telescope-file-browser.nvim#382.

@juridiener
Copy link

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:

1. Launch live_grep

2. Start typing

3. Realize that you need to scope to directory

4. `<Ctrl-f>`

5. Select directory

6. Get returned to live_grep, scoped to the directory you selected and with your search args still populated

asciicast

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)

Hello, thank you for providing this code, i searched a long time ago about this solution. And it worked. But one thing would be great if you could help me. At the time if i search in live_grep for a string and then want filter the result to a folder. I got all the folders. Is it possible only to see the folder where the match was found und then filtering the folders...

@briandipalma
Copy link
Contributor

@juridiener Have you taken a look at my comment above (#2201 (comment))? Just use ^search/directory/to/look/in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement to performance, inner workings or existent features
Projects
None yet
Development

No branches or pull requests