Repository: sharkdp/fd (language: rust)

Issue:
Option to explicitly set path instead of relying on argument order is missing
I believe `fd` needs an option to specify the path to be searched instead of relying on argument order in order to act as a proper `find` replacement. A lot of scripts (e.g. the popular `fzf`) can take a pre-formed `find` command as a "template" to which the search pattern is tacked on, relying on the fact that pattern comes last.

For example, `fzf` defines a variable `$dir` that can be dropped in to the search command template before the pattern is tacked on. With fd, this is unfortunately not workable since the path(s) must be the last parameter(s).

I know I can use a glob combined with `--full-path`  but then a separate search expression can't be supplied by fzf to be plugged in to the command line.


Discussion / hints:
Thank you for your feedback.

> I believe fd needs an option to specify the path to be searched instead of relying on argument order in order to act as a proper find replacement. A lot of scripts (e.g. the popular fzf) can take a pre-formed find command as a "template" to which the search pattern is tacked on, relying on the fact that pattern comes last.

I don't think we should use overly-restrictive templates as a guide to design `fd`'s command-line interface. I would much rather work on lifting that restriction instead of introducing a completely new command line option just for the sake of integration with other tools.

> For example, fzf defines a variable $dir that can be dropped in to the search command template before the pattern is tacked on. With fd, this is unfortunately not workable since the path(s) must be the last parameter(s).

Could you explain in a little bit more detail what you are trying to achieve? I use `fd` with `fzf` without any issues.
Sorry, I was using (I think) an old version of the fzf fish bindings which had the path tacked on and I confused myself from there.

I _think_ I agree with what you are saying, though I'm a bit confused because isn't the reliance on positional parameters a form of a restrictive template itself? Named arguments may be supplied in any order, but position-only parameters cramp one's style (a tiny bit).

I like the (current) C# approach where the first _n_ arguments may be supplied by position, but as soon as a named argument is provided only named arguments may follow. e.g.:

Given 

```csharp
int foo(int bar1, int bar2, int bar3, int bar4 = 42) { ... }
```

These are all equivalent:
```csharp
foo(1, 2, 3);
foo(1, 2, 3, 42);
foo(1, 2, bar3: 3);
foo(bar1: 1, bar2: 2, bar3: 3, bar4: 42);
foo(bar4: 42, bar3: 3, bar1: 1, bar2: 2);
```
btw, thanks for `hyperfine`. Just used it to surprise myself [here](https://github.com/fish-shell/fish-shell/commit/d8a1928c243b35fd399a56d69ed526f8c2b64a0a) and speed up fish shell's lookups against a list of reserved keywords some 10%. I would have thought searching a fixed, read-only sorted list of 40 strings would be faster via binary search than a hash table due to cache size and data locality but I was wrong!
> Sorry, I was using (I think) an old version of the fzf fish bindings which had the path tacked on and I confused myself from there.

Okay - so this particular `fd`+`fzf` problem is solved?

> I think I agree with what you are saying, though I'm a bit confused because isn't the reliance on positional parameters a form of a restrictive template itself? 

Fair point :smile:

> Named arguments may be supplied in any order, but position-only parameters cramp one's style (a tiny bit).

Agreed. For programming languages, I'm also a big fan of named arguments and often prefer to make the argument name explicit (like in your lower C# examples). However, for `fd` it's also a little bit about conciseness + typing speed, and we haven't experienced any major drawbacks with the current interface (up to now).

> btw, thanks for hyperfine. Just used it to surprise myself here and speed up fish shell's lookups against a list of reserved keywords some 10%. I would have thought searching a fixed, read-only sorted list of 40 strings would be faster via binary search than a hash table due to cache size and data locality but I was wrong!

Thanks! Glad to see that it's helpful.
In grep (at least GNU grep) normally it uses positional arguments, with the pattern first, followed by the files. However, there is a `-e` option to specify the pattern (I think e is for "expression"), so that you can put the pattern at any position if it is prefixed by `-e `. Something similar for fd would resolve the issue @mqudsi brought up. Although I don't know how difficult it would be to do that with clap. 
If someone wants to give this a try, here is a short summary:

Let's try to add a (possibly hidden) `--search-path <path>` option that would change `fd`'s USAGE from:
```
fd [FLAGS/OPTIONS] [<pattern>] [<path>...]
``` 
to:
```
fd [FLAGS/OPTIONS] --search-path <path> [<pattern>]
```

Fix patch:
diff --git a/src/app.rs b/src/app.rs
index 5e3cb06ee..be9967cb8 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -183,6 +183,14 @@ pub fn build_app() -> App<'static, 'static> {
         )
         .arg(arg("pattern"))
         .arg(arg("path").multiple(true))
+        .arg(
+            arg("search-path")
+                .long("search-path")
+                .takes_value(true)
+                .conflicts_with("path")
+                .multiple(true)
+                .number_of_values(1),
+        )
 }
 
 #[cfg_attr(rustfmt, rustfmt_skip)]
@@ -314,5 +322,9 @@ fn usage() -> HashMap<&'static str, Help> {
         , "Limit results based on modification time within the duration provided:\n    \
              using a duration: <NUM>d <NUM>h <NUM>m <NUM>s (e.g. 10h, 1d, 35min...)\n    \
              or a date and time: YYYY-MM-DD HH:MM:SS");
+    doc!(h, "search-path"
+        , "Provide paths to search as flag arguments rather than positional arguments."
+        , "Provide paths to search as flag arguments, preventing the usage of any positional `path` arugments.\n\
+           Changes the usage to `fd [FLAGS/OPTIONS] --search-path <path> --search-path <path2> [<pattern>]`");
     h
 }
diff --git a/src/main.rs b/src/main.rs
index dcb55a19a..3807da62b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -62,7 +62,10 @@ fn main() {
     }
 
     // Get one or more root directories to search.
-    let mut dir_vec: Vec<_> = match matches.values_of("path") {
+    let mut dir_vec: Vec<_> = match matches
+        .values_of("path")
+        .or(matches.values_of("search-path"))
+    {
         Some(paths) => paths
             .map(|path| {
                 let path_buffer = PathBuf::from(path);


Test patch: