BurntSushi / ripgrep

ripgrep recursively searches directories for a regex pattern while respecting your gitignore
The Unlicense
48.06k stars 1.98k forks source link

Support persistent configuration (`.rgrc`?) #196

Closed SimenB closed 6 years ago

SimenB commented 7 years ago

I'd love to keep different types for instance, and not have to specify --type-add each time.

My specific case is type zsh which searches zsh config files (.zshrc *.zsh, and some custom .zsh*).

Maybe it could support more stuff like no-heading, --heading etc as well, which would allow people to customize rg more.

(I know I can just alias rgzsh="rg --type-add 'zsh:*.zsh' --type-add 'zsh:.zshrc' --type zsh", but meh)

Loving the tool, btw!

BurntSushi commented 7 years ago

Can you make do with creating an alias? Or even a script in your ~/bin? For example, I have ~/bin/rgp:

#!/bin/sh

exec rg -p "$@" | less -RFX
SimenB commented 7 years ago

I just updated the OP with alias stuff ๐Ÿ˜„

And yes, I could, but then I have to remember my aliases (woe is me, right?). Doing rg -tzsh is more natural when I've gotten used to types than doing rgzsh or whatever.

rgp makes sense, though!

BurntSushi commented 7 years ago

Why don't you just alias rg itself? This works, for example:

alias rg="rg --type-add 'zsh:*.zsh' --type-add 'zsh:.zshrc'"

Then you can do rg -tzsh.

(Also, zsh seems like a common enough type that it should be part of ripgrep proper! :-))

SimenB commented 7 years ago

Aliasing rg seems pretty good!

I can provide a PR for zsh type (just add an entry here I suppose: https://github.com/BurntSushi/ripgrep/blob/master/src/types.rs)

But I'd still really like for that to be defined for rg, and not drowned amongst all of my other aliases (I still do alias | rg whatever since I forget. I'd like to think rg --type-list is nicer (and yes, I realize rg --type-list would work with your suggested rg alias)).

BurntSushi commented 7 years ago

I can provide a PR for zsh type (just add an entry here I suppose: https://github.com/BurntSushi/ripgrep/blob/master/src/types.rs)

Sounds good!

But I'd still really like for that to be defined for rg, and not drowned amongst all of my other aliases (I still do alias | rg whatever since I forget. I'd like to think rg --type-list is nicer (and yes, I realize rg --type-list would work with your suggested rg alias)).

You can also do command -V rg to see the alias.

SimenB commented 7 years ago

Conclusion: I can achieve what I want using simple aliases. I'd still like an .rgrc file though, to match .ackrc, .ptconfig.toml etc. ๐Ÿ˜„ I do understand if you feel like aliasing is enough, though

SimenB commented 7 years ago

Or maybe support $RG_OPTIONS to match grep? It just seems more explicit than aliasing rg itself.

BurntSushi commented 7 years ago

I do feel like aliases are a pretty good solution to this problem, and I'd like to stick with them for now.

I think we should leave this ticket open so that others can chime in. For example, aliases may not be easy to define on all platforms.

SimenB commented 7 years ago

BTW, is it possible to not recurse through directories? Case in point is searching for something in a zsh file in$HOME. Having rg recurse through everything is slow as hell (naturally).

Additionally: Is it possible to add ignore patterns? I managed to hit chrome application cache, which is an absolutely monstrous file

BurntSushi commented 7 years ago

BTW, is it possible to not recurse through directories?

Yes:

    --maxdepth NUM
        Descend at most NUM directories below the command line arguments.
        A value of zero only searches the starting-points themselves.

Additionally: Is it possible to add ignore patterns? I managed to hit chrome application cache, which is an absolutely monstrous file

Yes. Create a .ignore file and add patterns to that. (Or add them to your .gitignore.)

SimenB commented 7 years ago

Ok, but not add ignore to the execution itself?

Will an .rgignore file be respected? I just feel having an .ignore file in $HOME might be confusing if it's just used for rg

BurntSushi commented 7 years ago

Ok, but not add ignore to the execution itself?

I don't understand the question.

Will an .rgignore file be respected?

Please use .ignore. .rgignore is deprecated. Where did you see .rgignore?

I just feel having an .ignore file in $HOME might be confusing if it's just used for rg

I don't understand what's confusing about it. The Silver Searcher supports the same format.

The .ignore file can be anywhere. It could live right next to the thing you want to ignore. It works just like gitignore.

BurntSushi commented 7 years ago

Perhaps you're looking for the -g/--glob flag?

SimenB commented 7 years ago

I don't understand the question.

I want to do e.g. rg --ignore-dir 'Application Cache' or something like it, not have to create a file on disk.

Where did you see .rgignore?

Nowhere, it was just a name proposal. I can guess you don't like it though ๐Ÿ˜

Perhaps you're looking for the -g/--glob flag?

That should work! Just rg -g '!Application Cache/'?

BTW, using --maxdepth 0 never works, I always get

No files were searched, which means ripgrep probably applied a filter you didn't expect. Try running again with --debug

Doing --maxdepth 1 seems to just search current dir, though. Bug or should the man file get an update?

image

BurntSushi commented 7 years ago

That should work! Just rg -g '!Application Cache/'?

You probably want rg -g '!**/Application Cache/**', since the glob is applied to the full file path.

Doing --maxdepth 1 seems to just search current dir, though. Bug or should the man file get an update?

Seems correct to me. Read the docs again please:

    --maxdepth NUM
        Descend at most NUM directories below the command line arguments.
        A value of zero only searches the starting-points themselves.

A value of zero only searches the starting points. So if you run rg --maxdepth 0 foo, then the starting point is the current directory, which obviously can't be searched, since it isn't file. If you do rg --maxdepth 1 foo, then it descends one level. These are the same semantics as find, e.g., see the output of find ./ -maxdepth 0 and find ./ -maxdepth 1.

SimenB commented 7 years ago

Ah of course, so --maxdepth 0 is only when searching a file? Makes sense when I read it again!

Thanks for answering all of my questions, super helpful! ๐Ÿ˜„ Especially the -g one will be useful

BurntSushi commented 7 years ago

Ah of course, so --maxdepth 0 is only when searching a file?

--maxdepth can be used any time. All it does is control the number of times it descends into a directory. If the value is zero, then it will never descend into any directories. Therefore, since rg foo is equivalent to rg foo ./, the directory ./ isn't descended into, so there's nothing to search.

It's most likely that a value of 0 is only useful in a generic context, e.g., when you don't know if your file parameters will be files, directories or a mixture and you don't want to do any directory recursion.

SimenB commented 7 years ago

A more useful thing for --maxdepth doc might be "A value of one will only search files in the passed in directory" or something like it. But now I know, so I'm good ๐Ÿ˜„

kastiglione commented 7 years ago

I've hit one issue with using an alias: zsh completion doesn't work when I've shadowed the rg command with a rg alias. When I unalias rg the completion works. There's probably some way to get the zsh completion system to handle this case, but I don't yet know how to do that.

EDIT: The answer is setopt complete_aliases.

gvol commented 7 years ago

Just my two cents, but I really like .ackrc because it's used at the command line and when invoked by other tools like Emacs (a place that aliases don't work).

BurntSushi commented 7 years ago

In that case, can you put a wrapper script in your $HOME/bin?

On Nov 23, 2016 00:08, "Ivan Andrus" notifications@github.com wrote:

Just my two cents, but I really like .ackrc because it's used at the command line and when invoked by other tools like Emacs (a place that aliases don't work).

โ€” You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/BurntSushi/ripgrep/issues/196#issuecomment-262435651, or mute the thread https://github.com/notifications/unsubscribe-auth/AAb34niAsM6kJe2XlAWQGP9sFz0-gg5dks5rA8pWgaJpZM4Kieh1 .

BurntSushi commented 7 years ago

I know of at least one Windows user who wants this pretty badly. (cc @retep998) It sounds like the claim is that, on Windows at least, defining aliases and/or wrapper scripts is neither common nor convenient to do. Is this others' experience as well? Could someone please outline exactly what one has to do on Windows to create a wrapper script? Is it really an unreasonable expectation?

If we go down this path, we should consider the following:

IMO, the above implies significant complexity and likely an easy source of bugs. If we moved forward with this, I think I'd have to demand a rock solid specification (which I'm not inclined to work on any time soon) or some kind of conscious concessions at least (like, "not every option is overridable," for example, which seems like bad UX honestly).

BurntSushi commented 7 years ago

314 asks for project specific configs. Part of the initial design for this should address that (which may include punting on it).

(I still remain skeptical of the whole idea. See my previous comment.)

kbknapp commented 7 years ago

We may need to solve problems like "I set --context 5 in my config file, but I want to override occasionally with --context 0 on the command line." We can't just trivially merge the config with the CLI parameters since rg --context 5 --context 0 foo is illegal today. This is probably true of several other flags, and many other flags probably need the opposites added. e.g., If --no-ignore is in the config file, how does a user re-enable ignore functionality?

This is the primary discussion in kbknap/clap-rs#748

I've got some ideas for implementation I just haven't had to time to sit down and do it. The way it'll end up working if you use the clap implementation for configs is ripgrep would be responsible for reading in the config file off the disk (initially TOML) and hand off a supported struct to clap as an "external argv" (such as a TomlTable). clap then handles all the merging/overriding of options where whats passed on the command line manually will override any of these "external argv"s.

Any number of these external argv's could be given to clap (think global and per project, such as a current directory .rgrc), and will be evaluated in order for example:

let global = /* read global .rgrc and parse into TOML */;
let project = /* read current dir .rgrc and parse into TOML */;
app.external_argv(global)
    .external_argv(project)
    .get_matches()

(The name external_argv is still somewhat bikeshedable and will probably be implemented with generic fn external_arg<T: Into<ArgvExt>>(mut self, argv: T) -> Self and providing impls to convert from TomlTable, &str as the two initially supported types of external argvs. The other option is to have separate methods for all the diferent types of "external argvs" which is easier to document for things like simple strings).

Where the command line overrides PROJECT and PROJECT overrides GLOBAL.

The alternative is passing clap a Path and having it do all the reading and such, but I personnally would rather leave that to the consuming binary since there's so much that could go wrong, or be done differently, or custom errors, just too much I don't particularly want to do with clap. But parsing a Toml/Yaml/&str/etc against the valid args seems like it's well within the scope of clap.

There are some UX oddities which are initilaly may seem confusing, but IMO make sense when you think about them.

Most importantly, what clap calls flags would need to be considered very carefully. Because putting --foo in a config is impossible to "undo" unless there is a corresponding overriding or negating flag. Reason being, if the user doesn't provide the flag manually do they simply agree with the default, or did they leave off the flag becasue they don't want to use it?

Positional/Free args wouldn't be supported at all and would have to be input on the command line (although they could have default values). This is to prevent a user running rg <foo> and not realizing it's actually running rg <bar> <foo> because <bar> is defined in a config somewhere.

BurntSushi commented 7 years ago

@kbknapp Thanks for chiming in! As far as the mechanics go, all of that sounds very reasonable. It sounds like we both agree on what the essential challenges are too. For example, it would be unfortunate to have to add negations of every flag. However, the biggest reason why I'd find that unfortunate is because of the space it would take up in the output of --help, and less because of the presence of the flag itself. (Although that's still painful, unless there's a way to encapsulate the negation on the clap side of things.) That might suggest a possible avenue forward, but technically, a good chunk of it is possible today since clap supports adding flags that are hidden from the output of --help.

kbknapp commented 7 years ago

Although that's still painful, unless there's a way to encapsulate the negation on the clap side of things.

I see two possibilities with pros and cons to each (also, both would be possible concurrently).

For the specifics, a negation flag would simple take the long version of the regular flag and pre-pend no, for exmaple --follow would get --no-follow. If a flag only specifies a short version (ripgrep doesn't have any examples of this), the no would be prepended to the short such as -L gets --no-L.

My personal opinion is that having hidden negation flags is best, as it doesn't clutter the --help output and could quietly mentioned where applicable, such as when speaking of config files To override a flag specified in a config file, prepend --no to the flag, such as --no-follow. Done. It's not something that everyone needs to know unless they're messing with things like aliases and config files.

Of course, when using something wide sweeping like OverridableFlagsHidden you'll end up with flags that technically have corresponding negation flags that are functionally useless, but pragmatically it shouldn't affect anything.

If something like that would help this issue (of course once I finish implementing the external argv portion), I'd be happy supporting that as they'd both be technically very easy to implement.

BurntSushi commented 7 years ago

@kbknapp That sounds plausible. But if a flag is already preceded with no- (e.g., --no-ignore), then you'd wind up with --no-no-ignore. :P

Personally, I think the external argv portion is the most important. I'd be OK just starting there and seeing how it all shakes out, even if that means me adding the no flags manually.

BurntSushi commented 7 years ago

But if a flag is already preceded with no- (e.g., --no-ignore), then you'd wind up with --no-no-ignore. :P

Note that this could actually be reasonable. It seems funny, but it's consistent and predictable.

ssokolow commented 7 years ago

@SimenB If it helps, I wrote an rg wrapper function for zsh (which should work on bash with little or no modification) which implements ~/.config/rgrc as a way to prepend arguments onto my rg command line.

(It also maps -G ... to -g !... to work around a zsh footgun regarding starting arguments with !)

The alias: https://github.com/ssokolow/profile/blob/master/home/.zshrc.d/rg

Example rgrc: https://github.com/ssokolow/profile/blob/master/home/.config/rgrc

I'm still looking for documentation on how to generate shell completions when building from source, so I don't know what effect it'll have on completion support in its current state.

BurntSushi commented 7 years ago

@ssokolow That's a nice hack, and I'd encourage folks to try and use it if it helps.

The shell completion files are generated automatically and placed in target/release/build/ripgrep-*/out/.

mcepl commented 7 years ago

Without vote how this should be resolved, could I at least ask that if there is any configuration could it be stored in XDG compliant location (e.g., ~/.config/ripgrep), please?

BurntSushi commented 7 years ago

@mcepl If ripgrep has it, then it will definitely satisfy XDG on platforms where that makes sense. On platforms where it doesn't make sense, I don't know what to do. See: https://github.com/BurntSushi/ripgrep/issues/196#issuecomment-262853109

mcepl commented 7 years ago

Yeah, thatโ€™s a problem. I have decided in jbrout that we use glib anyway (because of Gtk), so I can use whatever directories it gives me on Windows or Mac, but I guess you donโ€™t want to depend on Glib.

ssokolow commented 7 years ago

On platforms where it doesn't make sense, I don't know what to do.

The app_dirs crate will handle that for you.

Just specify AppDataType::UserConfig and you'll get ${XDG_CONFIG_HOME:-$HOME/.config}/ripgrep on XDG platforms, $HOME/Library/Application Support/ripgrep on OSX, and %APPDATA%\BurntSushi\ripgrep on Windows.

EDIT: ...or you could step up a layer and use the preferences crate which depends on it.

BurntSushi commented 7 years ago

I guess you donโ€™t want to depend on Gli

You could say that. :-) perhaps we can borrow the logic that glib uses for Windows?

BurntSushi commented 7 years ago

@ssokolow nice! Thanks for that.

ssokolow commented 7 years ago

No problem. I'd actually have made a mention ages ago, but I was caught in that "Judging by ripgrep, he's clearly much smarter and more knowledgeable than me. He must already know this." trap.

BurntSushi commented 7 years ago

@ssokolow Haha. Indeed, that is a trap! Glad to be reminded of it.

Now that I think of it, I also had a kinda-sorta feeling that clap might just up and solve this problem for us. More thought still needs to be given to how config files actually work though. e.g., If you specify --no-ignore by default, how do you override it? Do we need to add a new flag? Some flags are have that like -i/-s/-S, but not all do.

kbknapp commented 7 years ago

I still intend to work on this issue in clap, but my bandwidth has been very limited lately with my day job. Unfortunately, I can't give a timeline for when I'll have it done. :-(

BurntSushi commented 7 years ago

@kbknapp Oh heavens, please do no worry one little bit! I think I already owe you several <drinks-of-your-choice>. :-)

sgraham commented 7 years ago

Oops, missed this issue when I filed mine. In my case, I'd be fine with a wrapper script on Linux/Mac, but on Windows, while making a .bat file "works", it's annoying in typical use because of the unfortunate "Terminate batch job (Y/N)?" prompt that happens if you Ctrl-C during a search.

So I'd enjoy an rc or an env var also. (Or just changing the default behaviour to --no-heading. ;)

BurntSushi commented 7 years ago

Are .bat files the only way to create simple scripts in Windows? Surely there must be something more convenient?

sgraham commented 7 years ago

Oh, you asked about aliases on Windows too -- it's possible via something like doskey rg=rg-win.exe --myoptions $*, but unfortunately there's no standard ".bashrc"-alike for cmd, so there's nowhere to put those and have them always available.

elirnm commented 7 years ago

@BurntSushi In Powershell you can write a function in a Powershell script and have Powershell load the module on startup, but that's arguably more complicated than a simple batch script (not to mention it doesn't work in cmd.exe, although it does avoid the annoying "terminate batch jobs" prompt). .bat/.cmd files are essentially the equivalent of .sh scripts on Unix. Powershell scripts are nicer to use than .bat/.cmd scripts and are more integrated into the shell but require the use of Powershell over Command Prompt and are a bit more work to write if you want them to be nicely loaded as a module.

They're not really too inconvenient and work perfectly fine except for the "terminate batch jobs" prompt that gets more annoying the more you run a command, especially for search where you might terminate with ctrl+c a fair amount.

elirnm commented 7 years ago

If you're fine with being restricted to Powershell, creating a wrapper function isn't actually too difficult. All you need to do is add a function to your profile, which will then automatically be loaded on starting Powershell.

So you could add a function like

function rg2 () {
  rg --no-heading $args
}

and just call rg2 from the command line to get --no-heading by default (although be sure to provide the full path to rg.exe if you want to actually name the function rg). Command Prompt is unfortunately not as nicely integrated with anything, as far as I'm aware.

Of course, running it from Powershell with everything using the default color schemes will make the file names unreadable https://github.com/BurntSushi/ripgrep/issues/342.

BurntSushi commented 7 years ago

@sgraham @elirnm I think what I'm hearing is that there is reasonably solid motivation for adding a config file to ripgrep, so I think I'm sold on that. (Others have mentioned Windows being difficult to write wrapper scripts in, but I don't think I ever got the full scoop.)

BatmanAoD commented 7 years ago

I think it makes sense to wait for clap to handle this.

For Windows, it sounds like part of the problem with the "just use an alias or similar customization" solution is that PowerShell is much easier to personalize than cmd.exe, but most Windows users (including myself) are reluctant to learn a new shell (or have never used a decent shell in the first place and don't understand the benefits) and therefore either stick to cmd.exe or use something like git-bash or Cygwin (which come with a host of complications and oddities, but are better than raw cmd.exe). I know that personalizing cmd.exe with alias-like things is possible*, but every time I think about trying it and look up the steps, it seems like it's more trouble than it's worth.

As an example: recently, I learned the hard way that in git-bash, ln -s silently copies the target to the new "symlink" file, rather than making a true symlink. Here is complete workaround in all its gory glory. Relatedly, once a real directory symlink is made in Windows, del (which typically behaves like rm) will recursively delete the entire linked directory. The only way to actually delete just the symlink* is to use...rmdir. Really. Fun times!

teeberg commented 7 years ago

Another argument against using an alias is that config files might allow you to have per-project settings, as ack lets you have by creating an .ackrc right in your project folder.

It should be doable to do that with a bash function though, so I may be able to help myself.

Reading the above thread, it sounds like this is not a use case that's been considered or planned so far, so please consider this a feature request while planning this. :-)

BurntSushi commented 7 years ago

@teeberg Thanks! That is a good one, and certainly seems like a logical addition. But it is good to keep in mind!

wsdjeg commented 7 years ago

@BurntSushi really thanks for this great tool, I am author of spacevim, and I am writting a background search tools for spacevim, now we support: ag, rg, pt, ack and grep. and I use neovim's job api to implement this feature. in ag, pt, ack the line number is enable by default, but when use rg, I need to add an extra argv -n to jobstart().

and here is the PR https://github.com/SpaceVim/SpaceVim/pull/699 and here is the fix commit for rg support in spacevim searching tools https://github.com/SpaceVim/SpaceVim/pull/699/commits/b30a3bb7d6af71b66e89a8a2ca9e085411444b77

what I hope is -n will be enabled by default.

sorry, I think it is not a bug, so I am not sure if I should open issue for it.