Compare commits

...

83 Commits

Author SHA1 Message Date
Andrew Gallant
8b6a3bc858 ci: unpin nightly, redux 2017-08-23 20:05:27 -04:00
Andrew Gallant
e10544f819 0.6.0 2017-08-23 19:54:50 -04:00
Andrew Gallant
dc7e39a6ba cargo: add crates.io badges 2017-08-23 19:54:33 -04:00
Andrew Gallant
36c16eb00c bump deps 2017-08-23 19:52:13 -04:00
Andrew Gallant
fffee61f80 changelog 0.6.0 2017-08-23 19:49:35 -04:00
Andrew Gallant
4cfb2b515b ci: bump rustc from 1.16 to 1.17
... and unpin the nightlies.
2017-08-23 19:49:02 -04:00
Andrew Gallant
398326bfe2 doc: note that ripgrep may terminate unexpectedly
Fixes #581
2017-08-23 19:14:27 -04:00
Andrew Gallant
01358a155c man: synchronize man page with --help 2017-08-23 19:13:52 -04:00
Andrew Gallant
30ca3ecca6 ci: strip ripgrep binary on Unix
This commit strips the ripgrep binary release artifact produced by CI
for Unix.

Fixes #413
2017-08-23 18:07:41 -04:00
Lilian A. Moraru
dbc91644fd Types extension and Yocto renaming to BitBake 2017-08-23 17:52:24 -04:00
Andrew Gallant
73c9ac4da5 integration tests: ignore regression_428 on Windows
The test is severely constrained to the specific ANSI formatting of
ripgrep in accordance with its default color scheme. The default color
scheme on Windows changed, which caused the test to fail.

For now, just disable the test on Windows.
2017-08-23 17:49:40 -04:00
Henri Sivonen
fe7fe74b0a Pass the simd-accel feature to encoding_rs 2017-08-20 08:42:31 -04:00
Gergő Pintér
3d9acdab18 Add short key for julia type
Fixes #574
2017-08-16 10:46:13 -04:00
dana
40bacbcd7c Add -x/--line-regexp (#520)
add -x/--line-regexp flag
2017-08-09 06:53:35 -04:00
Vurich
b3a9c34515 Remove unused libc dependency 2017-08-08 07:03:58 -04:00
Andrew Gallant
972ec1adc6 bump clap to 2.26
Fixes #482
2017-07-30 18:04:49 -04:00
Igor Gnatenko
a2d4c03c71 bump encoding_rs to 0.6 2017-07-30 18:00:50 -04:00
dana
b7c3cf314d Add test for option-arguments with leading hyphens 2017-07-30 17:55:24 -04:00
dana
6dce04963d Allow options with non-numeric arguments to accept leading hyphens in arguments (fixes #568) 2017-07-30 17:55:24 -04:00
dana
d4b790fd8d Install zsh APT package 2017-07-26 09:30:14 -04:00
dana
9283dd122e Update test_complete to source completion function for more reliable options parsing 2017-07-26 09:30:14 -04:00
dana
4c41e9225b Make completion support short-option values in same word; handle debug variable 2017-07-26 09:30:14 -04:00
Leonardo Santagada
9f2b054550 fix profile showing command for powershell
change the profile showing command to one that matches the microsoft article linked, as its simpler and the old one didn't work at least on windows 10 creators edition.
2017-07-22 08:53:46 -04:00
dana
5613df3034 Refactor zsh completion function
- Improve documentation
- Reorganise into functions
- Order options lexicographically
- Correct minor wording inconsistencies
- Fix --count error
- Fix --maxdepth error
- Fix --path-separator error
- Fix --version error
- Adjust exclusivity for --files, -h, -j, -o, -r, -t, -T, -v, -V, &c.
- Improve pattern-operand guard behaviour
- Partially fix issue with colorspec state
- Fix issue with typespec state
- Add completion for <type>:include: sequence
- Move licence info out of the way
2017-07-18 07:03:36 -04:00
dana
79ad81626f Update test_complete.sh to avoid false positives related to shell syntax 2017-07-18 07:03:36 -04:00
Lincoln Atkinson
354a5cad97 Fix invisible file path text in PowerShell (#557)
change default path color on Windows

This avoids a conflict with a PowerShell configuration that causes text to be invisible.

Fixes #342
2017-07-17 18:01:13 -04:00
Andrew Gallant
92e5fad27d ignore-0.2.2 2017-07-17 17:56:33 -04:00
Andrew Gallant
f86f987d71 benchsuite: fix another bug 2017-07-17 09:28:49 -04:00
Andrew Gallant
bfbd53eb92 benchsuite: fix bugs
This fixes a few bugs in the benchsuite script that have apparently
cropped up over time due to insufficient testing.

Fixes #558
2017-07-17 08:21:42 -04:00
Behnam Esfahbod
0668c74ed4 [ignore] Fix matched_path_or_any_parents() for patterns ending in slash
In `matched_path_or_any_parents()` implementation, we missed the point
that when we start walking up the tree, we have to set `is_dir` to
`true`, so path `ROOT/a/b/c` matches pattern `/a/`, although the
original path is not a dir.
2017-07-13 08:44:09 -04:00
Andrew Gallant
1c03298903 bump ignore version, take 2 2017-07-12 22:26:59 -04:00
Andrew Gallant
e0e8f26c56 bump ignore version 2017-07-12 22:26:23 -04:00
Andrew Gallant
f5337329f4 ignore-0.2.1 2017-07-12 22:08:53 -04:00
Behnam Esfahbod ✅
84f4b4ef68 [ignore] Add extensive test for gitignore matching (#551)
[ignore] tests and new matched_path_or_any_parents method

The test data (gitignore rules and expected result) is based on the test
repo at <https://github.com/behnam/gitignore-test>.

The new `matched_path_or_any_parents` method fixes a bug
in gitignore matching where rules of form `<dir>/*` result in ignoring
only first-level files, but no deep files. This is not correct, as `<dir>/*`
matches the first-level directories under `<dir>`, resulting all to be
ignored. The new method fixes it by trying to match all parents in the
path against the gitignore rules.

The new method is necessary because it necessarily entails a
performance hit for trying to match all parents.
2017-07-12 22:06:08 -04:00
Carl George
aeac85389d update COPR name
I switched Fedora usernames, so new builds will be at a different URL.
2017-07-08 07:57:58 -04:00
Gabriel
9b3921098a Tweak long_version features output
This reuses the systemd convention of putting flags on a separate line.
All credit to okdana for the implementation.  Addresses #524.
2017-07-07 12:18:44 -04:00
dana
ad262f1146 Update --version output to show compile-time features
Fixes #524
2017-07-07 11:30:59 -04:00
dana
170c078440 Add -f to completions 2017-07-06 19:00:35 -04:00
dana
db044a058a Add test_complete.sh to CI tasks 2017-07-06 19:00:35 -04:00
dana
c1f8040b32 Add test_complete script to compare rg --help output to zsh completion function 2017-07-06 19:00:35 -04:00
Jordan Danford
c8a5a7a3f4 Fix minor grammar issues in docs for ignore::Walk 2017-07-06 06:58:14 -04:00
dana
dd3df0ded7 Add --iglob to zsh completion function 2017-07-03 08:21:38 -04:00
dana
62a182af78 Improve zsh completion function
- Add missing options
- Fix confusion between --count and --max-count
- Improve wording consistency (capitalisation, punctuation, contractions, &c.)
- Add completion for encodings
- Add completion for colour specs
- Add partial completion for type specs
2017-07-03 07:15:21 -04:00
Peter S Panov
4047d9db71 add --iglob flag
Working with Chris Stadler, implemented
https://github.com/BurntSushi/ripgrep/issues/163#issuecomment-300012592
2017-07-03 06:52:52 -04:00
Jordan Danford
4683a325fa Update version of ignore crate in README.md 2017-07-02 15:46:25 -04:00
Bryan Richter
b6f1e5db1a Add cabal files for Haskell packages 2017-06-27 16:24:02 -04:00
Andrew Gallant
9e51b18ac7 bump wincolor dep 2017-06-19 13:46:43 -04:00
Andrew Gallant
9d7b6eb09a wincolor-0.1.4 2017-06-19 13:26:15 -04:00
Alex Crichton
7763c98188 wincolor: Re-fetch the console on all calls
The primary motivation for this commit was rust-lang/cargo#4189 where dropping a
`wincolor::Console` would call `CloseHandle` to close the console handle. Cargo
creates a few `Console` instances so it ended up closing stdout a little
earlier as intended!

The `GetStdHandle` function returns handles I believe aren't intended to be
closed (as there's no refcounting). I believe libstd doesn't close these
handles.

This commit also moves to calling `GetStdHandle` on demand which libstd changed
to doing so recently as well, preventing caching of stale handles that change
over time with calls to `SetStdHandle`.
2017-06-19 12:57:45 -04:00
Evan.Mattiza
06393f888c fix word boundary w/ capture group
fixes BurntSushi/ripgrep#506. Word boundary search as arg had unexpected
behavior. added capture group to regex to encapsulate 'or' option search and
prevent expansion and partial boundary finds.

Signed-off-by: Evan.Mattiza <emattiza@gmail.com>
2017-06-15 06:55:55 -04:00
Sid-Ali Teir
e0989ef13b add yocto file types 2017-06-12 19:02:21 -04:00
Gent Bajraj
45e850aff7 Add julia as file type 2017-06-12 11:39:19 -04:00
Eric Nielsen
f2d1c582a8 Use clap's overrides_with and default_value_if
to better organize options. These are the changes:
- color will have default value of "never" if --vimgrep is given,
  and only if no --color option is given
- last overrides previous: --line-number and --no-line-number, --heading
  and --no-heading, --with-filename and --no-filename, and --vimgrep and
  --count
- no heading will be show if --vimgrep is defined. This worked inside
  vim actually because heading is also only shown if tty is stdout
  (which is not the case when rg is called from vim).

Unfortunately, clap does not behave like a usual GNU/POSIX in some
cases, as reported in https://github.com/kbknapp/clap-rs/issues/970
and https://github.com/kbknapp/clap-rs/issues/976 (having all the bells
and whistles, on the other hand). So we still have issues like rg
failing when same argument is given more than once (unless for the few
ones marked with `multiple(true)`), or having unintuitive precedence
rules (and probably non-intentional, just there because of clap's
limitations) like:
- --no-filename over --vimgrep
- --no-line-number over --column, --pretty or --vimgrep
- --no-heading over --pretty
regardless of the order in which options where given, where the desired
behavior would be that the last option would override the previous ones
given.
2017-06-12 10:29:38 -04:00
Brian Gianforcaro
ab70815ea2 Add "msbuild" filetype for msbuild related files
This commit adds a "msbuild" filetype grouping, with
a few different file types being mapped to this grouping:

- MSBuild project files: .csproj, .vcxproj, .fsproj, .proj
- MSBuild shared property files: .props
- MSBuild shared targets files: .targets
2017-06-12 07:48:31 -04:00
Brian Gianforcaro
27f97db510 Add .inl as one of the c++ file typee definitions.
.inl files are often used by convention to include
both inline functions, or function templates.
2017-06-12 07:48:31 -04:00
Taryn Hill
506ad1f3cf Add a cshtml ignore type 2017-06-06 18:08:42 -04:00
Eric Nielsen
13235b596f Use uppercase for required argument names
This reverts a couple of changes introduced in 4c78ca8 and keeps the
`PATTERN` argument consistently uppercased, so error messages can look
like:

    error: The following required arguments were not provided:
        <PATTERN>
2017-06-01 20:41:04 -04:00
Fangrui Song
2628c8f38e Add Zsh completion file 2017-05-29 16:55:03 -04:00
Andrew Gallant
112b3c5e0a Fix another bug in -o/--only-matching.
The handling of the -o/--only-matching was incorrect. We cannot ever
re-run regexes on a *subset* of a matched line, because it doesn't take
into account zero width assertions on the edges of the regex. This
occurs whenever an end user uses an assertion explicity, but also occurs
when one is used implicitly, e.g., with the `-w` flag.

This instead reuses the initial matched range from the first regex
match. We also apply this fix to coloring.

Fixes #493
2017-05-29 09:51:58 -04:00
Eric Nielsen
4c78ca8b70 Update help and man pages
Formatting of rg.1.md. Remove backticks from already indented code.
Add missing italic to some arguments, Replace -n by --line-number in
--pretty for better clarity. Add explicit example of `*.foo` instead of
`<glob>` in examples. Add vim information to --vimgrep.

In src/app.rs, also changed help text for pattern and regexp. Actually,
"multiple patterns may be given" was not true for the standalone
pattern.
2017-05-26 19:17:59 -04:00
Eric Nielsen
ff898cd105 Remove vestigial color function from src/args.rs
It's usage was replaced by the `color_choice` function. Also, `color`
was outdated, as it didn't include testing for the new "ansi" option.
2017-05-26 07:00:58 -04:00
Eric Nielsen
2c98e5ce1e Update documentation for --color ansi
In `src/app.rs`, change typo "When ansi used" to "When ansi is used".
Update man pages with missing `ansi` option for `--color`.
2017-05-25 18:58:11 -04:00
Eric Nielsen
1e3fc79949 Should show filename for one file with vimgrep
With vim configured with:

    set grepprg=rg\ --vimgrep
    set grepformat^=%f:%l:%c:%m

and running the command `:grep 'vimgrep' doc/rg.1`, the output should
be:

    doc/rg.1:446:8:.B \-\-vimgrep

but the actual output was:

    446:8:.B \-\-vimgrep

Same issue would happen if results only match one file. Ag behaves as
expected.
2017-05-24 23:12:16 -04:00
Eric Nielsen
d1bbc6956b Fix typo, should be 'mode' instead of 'more'
in man pages.
2017-05-24 17:51:42 -04:00
Manuel Vives
cd6c54f5f4 Add support for QMake files 2017-05-24 16:44:22 -04:00
Andrew Gallant
44c03f58bc bump deps, redux
This only bumps the regex dependency. The new clap version causes a bump
in unicode-segmentation, which in turn requires a Rust 1.15, which is
above ripgrep's currently supported minimum Rust version of 1.12.
2017-05-21 15:56:56 -04:00
Andrew Gallant
d1a6ab922e Revert "bump deps"
This reverts commit b860fa3acd.
2017-05-21 15:52:58 -04:00
Andrew Gallant
b860fa3acd bump deps 2017-05-21 12:33:13 -04:00
Marc Tiehuis
229b8e3b33 Make --quiet flag apply when using --files option
Fixes #483.
2017-05-19 20:00:47 -04:00
Andrew Gallant
a515c4d601 update brew tap 2017-05-11 20:11:13 -04:00
Andrew Gallant
5a666b042d bump ripgrep, ignore, globset
The `ignore` and `globset` crates both got breaking changes in the
course of fixing #444, so increase 0.x to 0.(x+1).
2017-05-11 19:12:20 -04:00
Andrew Gallant
16109166fe changelog 0.5.2 2017-05-11 19:05:40 -04:00
Andrew Gallant
0b685c8429 deps: update clap to 2.24
Fixes #442
2017-05-08 19:24:11 -04:00
Eli Miller
d2c7a76a3c Add Powershell tips and autocompletion instructions 2017-05-08 19:23:41 -04:00
Chris MacNaughton
20f7d9b3a2 Add snapcraft.yaml
[Snapcraft](https://snapcraft.io/) makes Linux packaging very simple in a cross-distro
way. This adds the snapcraft.yaml file to setup a snap of ripgrep
2017-05-04 08:30:22 -04:00
Roman Proskuryakov
362abed44a Fix reiteration of the first found match with --only-mathing flag
Fixes #451
2017-04-21 08:11:55 -04:00
Andrew Gallant
c50b8b4125 Add better error messages for invalid globs.
This threads the original glob given by end users through all of the
glob parsing errors. This was slightly trickier than it might appear
because the gitignore implementation actually modifies the glob before
compiling it. So in order to get better glob error messages everywhere,
we need to track the original glob both in the glob parser and in the
higher-level abstractions in the `ignore` crate.

Fixes #444
2017-04-12 18:14:23 -04:00
Andrew Gallant
7ad23e5565 Use for_label_no_replacement.
This will cause certain unsupported legacy encodings to act as if they
don't exist, in order to avoid using an unhelpful (in the context of
file searching) "replacement" encoding.

Kudos to @hsivonen for chirping about this!
2017-04-12 18:14:23 -04:00
Marc Tiehuis
66efbad871 Add dfa-size-limit and regex-size-limit arguments
Fixes #362.
2017-04-12 18:14:23 -04:00
Bryan Richter
1f2a9b0306 Add nix 2017-04-12 18:10:59 -04:00
Andrew Gallant
a45fe94240 update brew tap 2017-04-09 10:28:22 -04:00
Andrew Gallant
ac1c95a6d9 0.5.1 2017-04-09 09:47:00 -04:00
Andrew Gallant
685b431d80 bump deps 2017-04-09 09:46:37 -04:00
38 changed files with 2051 additions and 388 deletions

8
.gitignore vendored
View File

@@ -6,3 +6,11 @@ target
/ignore/Cargo.lock /ignore/Cargo.lock
/termcolor/Cargo.lock /termcolor/Cargo.lock
/wincolor/Cargo.lock /wincolor/Cargo.lock
# Snapcraft files
stage
prime
parts
*.snap
*.pyc
ripgrep*_source.tar.bz2

View File

@@ -3,19 +3,27 @@ language: rust
env: env:
global: global:
- PROJECT_NAME=ripgrep - PROJECT_NAME=ripgrep
- RUST_BACKTRACE: full
addons:
apt:
packages:
# Needed for completion-function test
- zsh
matrix: matrix:
include: include:
# Nightly channel. # Nightly channel.
# (All *nix releases are done on the nightly channel to take advantage # (All *nix releases are done on the nightly channel to take advantage
# of the regex library's multiple pattern SIMD search.) # of the regex library's multiple pattern SIMD search.)
- os: linux - os: linux
rust: nightly-2017-03-13 rust: nightly
env: TARGET=i686-unknown-linux-musl env: TARGET=i686-unknown-linux-musl
- os: linux - os: linux
rust: nightly-2017-03-13 rust: nightly
env: TARGET=x86_64-unknown-linux-musl env: TARGET=x86_64-unknown-linux-musl
- os: osx - os: osx
rust: nightly-2017-03-13 rust: nightly
env: TARGET=x86_64-apple-darwin env: TARGET=x86_64-apple-darwin
# Beta channel. # Beta channel.
- os: linux - os: linux
@@ -26,10 +34,10 @@ matrix:
env: TARGET=x86_64-unknown-linux-gnu env: TARGET=x86_64-unknown-linux-gnu
# Minimum Rust supported channel. # Minimum Rust supported channel.
- os: linux - os: linux
rust: 1.12.0 rust: 1.17.0
env: TARGET=x86_64-unknown-linux-gnu env: TARGET=x86_64-unknown-linux-gnu
- os: linux - os: linux
rust: 1.12.0 rust: 1.17.0
env: TARGET=x86_64-unknown-linux-musl env: TARGET=x86_64-unknown-linux-musl
before_install: before_install:
@@ -57,7 +65,7 @@ deploy:
# channel to use to produce the release artifacts # channel to use to produce the release artifacts
# NOTE make sure you only release *once* per target # NOTE make sure you only release *once* per target
# TODO you may want to pick a different channel # TODO you may want to pick a different channel
condition: $TRAVIS_RUST_VERSION = nightly-2017-03-13 condition: $TRAVIS_RUST_VERSION = nightly
tags: true tags: true
branches: branches:

View File

@@ -1,3 +1,91 @@
0.6.0 (2017-08-23)
==================
This is a new minor version release of ripgrep that includes many bug fixes
and a few new features such as `--iglob` and `-x/--line-regexp`.
Note that this release increases the minimum supported Rust version from 1.12
to 1.17.
Feature enhancements:
* Added or improved file type filtering for BitBake, C++, Cabal, cshtml, Julia,
Make, msbuild, QMake, Yocto
* [FEATURE #163](https://github.com/BurntSushi/ripgrep/issues/163):
Add an `--iglob` flag that is like `-g/--glob`, but matches globs
case insensitively.
* [FEATURE #520](https://github.com/BurntSushi/ripgrep/pull/518):
Add `-x/--line-regexp` flag, which requires a match to span an entire line.
* [FEATURE #551](https://github.com/BurntSushi/ripgrep/pull/551),
[FEATURE #554](https://github.com/BurntSushi/ripgrep/pull/554):
`ignore`: add new `matched_path_or_any_parents` method.
Bug fixes:
* [BUG #342](https://github.com/BurntSushi/ripgrep/issues/342):
Fix invisible text in some PowerShell environments by changing the
default color scheme on Windows.
* [BUG #413](https://github.com/BurntSushi/ripgrep/issues/413):
Release binaries on Unix are now `strip`'d by default. This decreases
binary size by an order of magnitude.
* [BUG #483](https://github.com/BurntSushi/ripgrep/issues/483):
When `--quiet` is passed, `--files` should be quiet.
* [BUG #488](https://github.com/BurntSushi/ripgrep/pull/488):
When `--vimgrep` is passed, `--with-filename` should be enabled
automatically.
* [BUG #493](https://github.com/BurntSushi/ripgrep/issues/493):
Fix another bug in the implementation of the `-o/--only-matching`
flag.
* [BUG #499](https://github.com/BurntSushi/ripgrep/pull/499):
Permit certain flags to override others.
* [BUG #523](https://github.com/BurntSushi/ripgrep/pull/523):
`wincolor`: Re-fetch Windows console on all calls.
* [BUG #523](https://github.com/BurntSushi/ripgrep/issues/524):
`--version` now shows enabled compile-time features.
* [BUG #532](https://github.com/BurntSushi/ripgrep/issues/532),
[BUG #536](https://github.com/BurntSushi/ripgrep/pull/536),
[BUG #538](https://github.com/BurntSushi/ripgrep/pull/538),
[BUG #540](https://github.com/BurntSushi/ripgrep/pull/540),
[BUG #560](https://github.com/BurntSushi/ripgrep/pull/560),
[BUG #565](https://github.com/BurntSushi/ripgrep/pull/565):
Improve zsh completion.
* [BUG #578](https://github.com/BurntSushi/ripgrep/pull/578):
Enable SIMD for `encoding_rs` when appropriate.
* [BUG #580](https://github.com/BurntSushi/ripgrep/issues/580):
Fix `-w/--word-regexp` in the presence of capturing groups.
* [BUG #581](https://github.com/BurntSushi/ripgrep/issues/581):
Document that ripgrep may terminate unexpectedly when searching via
memory maps (which can happen using default settings).
Friends of ripgrep:
I'd like to give a big Thank You to @okdana for their recent hard work on
ripgrep. This includes new features like `--line-regexp`, heroic effort on
zsh auto-completion and thinking through some thorny argv issues with me.
I'd also like to thank @ericbn for their work on improving ripgrep's argv
parsing by allowing some flags to override others.
Thanks @okdana and @ericbn!
0.5.2 (2017-05-11)
==================
Feature enhancements:
* Added or improved file type filtering for Nix.
* [FEATURE #362](https://github.com/BurntSushi/ripgrep/issues/362):
Add `--regex-size-limit` and `--dfa-size-limit` flags.
* [FEATURE #444](https://github.com/BurntSushi/ripgrep/issues/444):
Improve error messages for invalid globs.
Bug fixes:
* [BUG #442](https://github.com/BurntSushi/ripgrep/issues/442):
Fix line wrapping in `--help` output.
* [BUG #451](https://github.com/BurntSushi/ripgrep/issues/451):
Fix bug with duplicate output when using `-o/--only-matching` flag.
0.5.1 (2017-04-09) 0.5.1 (2017-04-09)
================== ==================
Feature enhancements: Feature enhancements:

175
Cargo.lock generated
View File

@@ -1,21 +1,20 @@
[root] [root]
name = "ripgrep" name = "ripgrep"
version = "0.5.0" version = "0.6.0"
dependencies = [ dependencies = [
"atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bytecount 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "bytecount 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.23.1 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding_rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"grep 0.1.6", "grep 0.1.6",
"ignore 0.1.9", "ignore 0.2.2",
"lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 0.3.2", "termcolor 0.3.2",
] ]
@@ -39,41 +38,42 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "0.8.2" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "bytecount" name = "bytecount"
version = "0.1.6" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"simd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "0.1.0" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.23.1" version = "2.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -83,18 +83,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.5.0" version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.4.2" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -104,46 +105,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "fs2" name = "fs2"
version = "0.4.1" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "globset" name = "globset"
version = "0.1.4" version = "0.2.0"
dependencies = [ dependencies = [
"aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "grep" name = "grep"
version = "0.1.6" version = "0.1.6"
dependencies = [ dependencies = [
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "ignore" name = "ignore"
version = "0.1.9" version = "0.2.2"
dependencies = [ dependencies = [
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"globset 0.1.4", "globset 0.2.0",
"lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@@ -158,17 +159,17 @@ dependencies = [
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "0.2.5" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.21" version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "log" name = "log"
version = "0.3.7" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -176,7 +177,7 @@ name = "memchr"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -184,36 +185,36 @@ name = "memmap"
version = "0.5.2" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "fs2 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.3.0" version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "regex" name = "regex"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"simd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "simd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.4.0" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -230,6 +231,11 @@ name = "simd"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "simd"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.6.0" version = "0.6.0"
@@ -237,11 +243,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "term_size" name = "term_size"
version = "0.2.3" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@@ -249,30 +255,30 @@ dependencies = [
name = "termcolor" name = "termcolor"
version = "0.3.2" version = "0.3.2"
dependencies = [ dependencies = [
"wincolor 0.1.3", "wincolor 0.1.4",
] ]
[[package]] [[package]]
name = "thread-id" name = "textwrap"
version = "3.0.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "0.3.3" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.1.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -282,7 +288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "unreachable" name = "unreachable"
version = "0.1.1" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -295,7 +301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "vec_map" name = "vec_map"
version = "0.7.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -325,7 +331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "wincolor" name = "wincolor"
version = "0.1.3" version = "0.1.4"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -335,35 +341,36 @@ dependencies = [
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" "checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
"checksum bytecount 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1e8f09fbc8c6726a4b616dcfbd4f54491068d6bb1b93ac03c78ac18ff9a5924a" "checksum bytecount 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4bbeb7c30341fce29f6078b4bdf876ea4779600866e98f5b2d203a534f195050"
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum clap 2.23.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d480c39a2e5f9b3a3798c661613e1b0e7a7ae71e005102d4aa910fc3289df484" "checksum clap 2.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2267a8fdd4dce6956ba6649e130f62fb279026e5e84b92aa939ac8f85ce3f9f0"
"checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97"
"checksum encoding_rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a1cca0a26f904955d80d70b9bff1019e4f4cbc06f2fcbccf8bd3d889cc1c9b7" "checksum encoding_rs 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e00a1b1e95eb46988805ceee6f34cd95c46a6753e290cb3ff0486931989d4a4c"
"checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83" "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
"checksum fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34edaee07555859dc13ca387e6ae05686bb4d0364c95d649b6dab959511f4baf" "checksum fs2 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ab76cfd2aaa59b7bf6688ad9ba15bbae64bff97f04ea02144cfd3443e5c2866"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4732c563b9a21a406565c4747daa7b46742f082911ae4753f390dc9ec7ee1a97" "checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
"checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135" "checksum libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "8a014d9226c2cc402676fbe9ea2e15dd5222cd1dd57f576b5b283178c944a264"
"checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad" "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" "checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
"checksum memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46f3c7359028b31999287dae4e5047ddfe90a23b7dca2282ce759b491080c99b" "checksum memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46f3c7359028b31999287dae4e5047ddfe90a23b7dca2282ce759b491080c99b"
"checksum num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a18c392466409c50b87369414a2680c93e739aedeb498eb2bff7d7eb569744e2" "checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584"
"checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01" "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
"checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" "checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
"checksum simd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63b5847c2d766ca7ce7227672850955802fabd779ba616aeabead4c2c3877023" "checksum simd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63b5847c2d766ca7ce7227672850955802fabd779ba616aeabead4c2c3877023"
"checksum simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a94d14a2ae1f1f110937de5fb69e494372560181c7e1739a097fcc2cee37ba0"
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
"checksum term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "07b6c1ac5b3fffd75073276bca1ceed01f67a28537097a2a9539e116e50fb21a" "checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
"checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" "checksum textwrap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f728584ea33b0ad19318e20557cb0a39097751dbb07171419673502f848c7af6"
"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7" "checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14"
"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3" "checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946"
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
"checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8cdc8b93bd0198ed872357fb2e667f7125646b1762f16d60b2c96350d361897" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff" "checksum walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "ripgrep" name = "ripgrep"
version = "0.5.0" #:version version = "0.6.0" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"] authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """ description = """
Line oriented search tool using Rust's regex library. Combines the raw Line oriented search tool using Rust's regex library. Combines the raw
@@ -16,6 +16,10 @@ license = "Unlicense/MIT"
exclude = ["HomebrewFormula"] exclude = ["HomebrewFormula"]
build = "build.rs" build = "build.rs"
[badges]
travis-ci = { repository = "BurntSushi/ripgrep" }
appveyor = { repository = "BurntSushi/ripgrep" }
[[bin]] [[bin]]
bench = false bench = false
path = "src/main.rs" path = "src/main.rs"
@@ -28,13 +32,12 @@ path = "tests/tests.rs"
[dependencies] [dependencies]
atty = "0.2.2" atty = "0.2.2"
bytecount = "0.1.4" bytecount = "0.1.4"
clap = "2.23.1" clap = "2.26"
encoding_rs = "0.5.0" encoding_rs = "0.6"
env_logger = { version = "0.4", default-features = false } env_logger = { version = "0.4", default-features = false }
grep = { version = "0.1.5", path = "grep" } grep = { version = "0.1.5", path = "grep" }
ignore = { version = "0.1.9", path = "ignore" } ignore = { version = "0.2.2", path = "ignore" }
lazy_static = "0.2" lazy_static = "0.2"
libc = "0.2"
log = "0.3" log = "0.3"
memchr = "1" memchr = "1"
memmap = "0.5" memmap = "0.5"
@@ -44,12 +47,12 @@ same-file = "0.1.1"
termcolor = { version = "0.3.0", path = "termcolor" } termcolor = { version = "0.3.0", path = "termcolor" }
[build-dependencies] [build-dependencies]
clap = "2.23.1" clap = "2.26"
lazy_static = "0.2" lazy_static = "0.2"
[features] [features]
avx-accel = ["bytecount/avx-accel"] avx-accel = ["bytecount/avx-accel"]
simd-accel = ["bytecount/simd-accel", "regex/simd-accel"] simd-accel = ["bytecount/simd-accel", "regex/simd-accel", "encoding_rs/simd-accel"]
[profile.release] [profile.release]
debug = true debug = true

View File

@@ -187,17 +187,17 @@ If you're a **Gentoo** user, you can install `ripgrep` from the [official repo](
$ emerge ripgrep $ emerge ripgrep
``` ```
If you're a **Fedora 24+** user, you can install `ripgrep` from [copr](https://copr.fedorainfracloud.org/coprs/carlgeorge/ripgrep/): If you're a **Fedora 24+** user, you can install `ripgrep` from [copr](https://copr.fedorainfracloud.org/coprs/carlwgeorge/ripgrep/):
``` ```
$ dnf copr enable carlgeorge/ripgrep $ dnf copr enable carlwgeorge/ripgrep
$ dnf install ripgrep $ dnf install ripgrep
``` ```
If you're a **RHEL/CentOS 7** user, you can install `ripgrep` from [copr](https://copr.fedorainfracloud.org/coprs/carlgeorge/ripgrep/): If you're a **RHEL/CentOS 7** user, you can install `ripgrep` from [copr](https://copr.fedorainfracloud.org/coprs/carlwgeorge/ripgrep/):
``` ```
$ yum-config-manager --add-repo=https://copr.fedorainfracloud.org/coprs/carlgeorge/ripgrep/repo/epel-7/carlgeorge-ripgrep-epel-7.repo $ yum-config-manager --add-repo=https://copr.fedorainfracloud.org/coprs/carlwgeorge/ripgrep/repo/epel-7/carlwgeorge-ripgrep-epel-7.repo
$ yum install ripgrep $ yum install ripgrep
``` ```
@@ -338,10 +338,17 @@ The syntax supported is
Shell completion files are included in the release tarball for Bash, Fish, Zsh Shell completion files are included in the release tarball for Bash, Fish, Zsh
and PowerShell. and PowerShell.
For **bash**, move `rg.bash-completion` to `$XDG_CONFIG_HOME/bash_completion` For **bash**, move `complete/rg.bash-completion` to `$XDG_CONFIG_HOME/bash_completion`
or `/etc/bash_completion.d/`. or `/etc/bash_completion.d/`.
For **fish**, move `rg.fish` to `$HOME/.config/fish/completions`. For **fish**, move `complete/rg.fish` to `$HOME/.config/fish/completions/`.
For **PowerShell**, add `. _rg.ps1` to your PowerShell
[profile](https://technet.microsoft.com/en-us/library/bb613488(v=vs.85).aspx)
(note the leading period). If the `_rg.ps1` file is not on your `PATH`, do
`. /path/to/_rg.ps1` instead.
For **zsh**, move `complete/_rg` to one of your `$fpath` directories.
### Building ### Building
@@ -385,7 +392,7 @@ from the repository root.
##### Powershell Profile ##### Powershell Profile
To customize powershell on start-up there is a special powershell script that has to be created. To customize powershell on start-up there is a special powershell script that has to be created.
In order to find its location run command `Get-Command $profile | Select-Object -ExpandProperty Definition` In order to find its location type `$profile`
See [more](https://technet.microsoft.com/en-us/library/bb613488(v=vs.85).aspx) for profile details. See [more](https://technet.microsoft.com/en-us/library/bb613488(v=vs.85).aspx) for profile details.
Any powershell code in this file gets evaluated at the start of console. Any powershell code in this file gets evaluated at the start of console.
@@ -423,12 +430,48 @@ Powershell special variables:
This alias checks whether there is **Stdin** input and propagates only if there is some lines. This alias checks whether there is **Stdin** input and propagates only if there is some lines.
Otherwise empty `$input` will make powershell to trigger `rg` to search empty **Stdin** Otherwise empty `$input` will make powershell to trigger `rg` to search empty **Stdin**
##### Piping non-ASCII content to ripgrep
When piping input into native executables in PowerShell, the encoding of the
input is controlled by the `$OutputEncoding` variable. By default, this is set
to US-ASCII, and any characters in the pipeline that don't have encodings in
US-ASCII are converted to `?` (question mark) characters.
To change this setting, set `$OutputEncoding` to a different encoding, as
represented by a .NET encoding object. Some common examples are below. The
value of this variable is reset when PowerShell restarts, so to make this
change take effect every time PowerShell is started add a line setting the
variable into your PowerShell profile.
Example `$OutputEncoding` settings:
* UTF-8 without BOM: `$OutputEncoding = [System.Text.UTF8Encoding]::new()`
* The console's output encoding:
`$OutputEncoding = [System.Console]::OutputEncoding`
If you continue to have encoding problems, you can also force the encoding
that the console will use for printing to UTF-8 with
`[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8`. This
will also reset when PowerShell is restarted, so you can add that line
to your profile as well if you want to make the setting permanent.
### Known issues ### Known issues
#### I just hit Ctrl+C in the middle of ripgrep's output and now my terminal's foreground color is wrong! #### I just hit Ctrl+C in the middle of ripgrep's output and now my terminal's foreground color is wrong!
Type in `color` on Windows and `echo -ne "\033[0m"` on Unix to restore your Type in `color` in cmd.exe (Command Prompt) and `echo -ne "\033[0m"` on Unix
original foreground color. to restore your original foreground color.
In PowerShell, you can add the following code to your profile which will
restore the original foreground color when `Reset-ForegroundColor` is called.
Including the `Set-Alias` line will allow you to call it with simply `color`.
```powershell
$OrigFgColor = $Host.UI.RawUI.ForegroundColor
function Reset-ForegroundColor {
$Host.UI.RawUI.ForegroundColor = $OrigFgColor
}
Set-Alias -Name color -Value Reset-ForegroundColor
```
PR [#187](https://github.com/BurntSushi/ripgrep/pull/187) fixed this, and it PR [#187](https://github.com/BurntSushi/ripgrep/pull/187) fixed this, and it
was later deprecated in was later deprecated in

View File

@@ -1,6 +1,7 @@
environment: environment:
global: global:
PROJECT_NAME: ripgrep PROJECT_NAME: ripgrep
RUST_BACKTRACE: full
matrix: matrix:
- TARGET: i686-pc-windows-gnu - TARGET: i686-pc-windows-gnu
CHANNEL: stable CHANNEL: stable

View File

@@ -1082,7 +1082,7 @@ def download_subtitles_en(suite_dir):
if not os.path.exists(en_path): if not os.path.exists(en_path):
if not os.path.exists(en_path_gz): if not os.path.exists(en_path_gz):
run_cmd(['curl', '-LO', SUBTITLES_EN_URL], cwd=subtitle_dir) run_cmd(['curl', '-LO', SUBTITLES_EN_URL], cwd=subtitle_dir)
run_cmd(['gunzip', en_path_gz], cwd=subtitle_dir) run_cmd(['gunzip', en_path_gz])
if not os.path.exists(en_path_sample): if not os.path.exists(en_path_sample):
# Get a sample roughly the same size as the Russian corpus so that # Get a sample roughly the same size as the Russian corpus so that
# benchmarks finish in a reasonable time. # benchmarks finish in a reasonable time.
@@ -1109,7 +1109,7 @@ def download_subtitles_ru(suite_dir):
if not os.path.exists(ru_path): if not os.path.exists(ru_path):
if not os.path.exists(ru_path_gz): if not os.path.exists(ru_path_gz):
run_cmd(['curl', '-LO', SUBTITLES_RU_URL], cwd=subtitle_dir) run_cmd(['curl', '-LO', SUBTITLES_RU_URL], cwd=subtitle_dir)
run_cmd(['gunzip', ru_path_gz], cwd=subtitle_dir) run_cmd(['gunzip', ru_path_gz])
def has_subtitles_ru(suite_dir): def has_subtitles_ru(suite_dir):
@@ -1184,6 +1184,7 @@ def collect_benchmarks(suite_dir, filter_pat=None,
name, name,
' '.join(['--download %s' % n for n in e.missing_names]), ' '.join(['--download %s' % n for n in e.missing_names]),
)) ))
continue
except MissingCommands as e: except MissingCommands as e:
fmt = 'missing commands: %s, skipping benchmark %s ' \ fmt = 'missing commands: %s, skipping benchmark %s ' \
'(run with --allow-missing to run incomplete benchmarks)' '(run with --allow-missing to run incomplete benchmarks)'
@@ -1239,7 +1240,7 @@ def main():
benchmarks = collect_benchmarks( benchmarks = collect_benchmarks(
args.dir, filter_pat=args.bench, args.dir, filter_pat=args.bench,
allow_missing_commands=args.allow_missing, allow_missing_commands=args.allow_missing,
disabled_cmds=args.disabled.split(','), disabled_cmds=(args.disabled or '').split(','),
warmup_iter=args.warmup_iter, bench_iter=args.bench_iter) warmup_iter=args.warmup_iter, bench_iter=args.bench_iter)
for b in benchmarks: for b in benchmarks:
print(b.name) print(b.name)
@@ -1266,7 +1267,7 @@ def main():
benchmarks = collect_benchmarks( benchmarks = collect_benchmarks(
args.dir, filter_pat=args.bench, args.dir, filter_pat=args.bench,
allow_missing_commands=args.allow_missing, allow_missing_commands=args.allow_missing,
disabled_cmds=args.disabled.split(','), disabled_cmds=(args.disabled or '').split(','),
warmup_iter=args.warmup_iter, bench_iter=args.bench_iter) warmup_iter=args.warmup_iter, bench_iter=args.bench_iter)
for i, b in enumerate(benchmarks): for i, b in enumerate(benchmarks):
result = b.run() result = b.run()

View File

@@ -22,6 +22,5 @@ fn main() {
let mut app = app::app(); let mut app = app::app();
app.gen_completions("rg", Shell::Bash, &outdir); app.gen_completions("rg", Shell::Bash, &outdir);
app.gen_completions("rg", Shell::Fish, &outdir); app.gen_completions("rg", Shell::Fish, &outdir);
app.gen_completions("rg", Shell::Zsh, &outdir);
app.gen_completions("rg", Shell::PowerShell, &outdir); app.gen_completions("rg", Shell::PowerShell, &outdir);
} }

View File

@@ -18,9 +18,13 @@ mk_tarball() {
mkdir "$td/$name" mkdir "$td/$name"
mkdir "$td/$name/complete" mkdir "$td/$name/complete"
cp target/$TARGET/release/rg "$td/$name/" cp target/$TARGET/release/rg "$td/$name/rg"
strip "$td/$name/rg"
cp {doc/rg.1,README.md,UNLICENSE,COPYING,LICENSE-MIT} "$td/$name/" cp {doc/rg.1,README.md,UNLICENSE,COPYING,LICENSE-MIT} "$td/$name/"
cp target/$TARGET/release/build/ripgrep-*/out/{_rg,rg.bash-completion,rg.fish,_rg.ps1} "$td/$name/complete/" cp \
target/$TARGET/release/build/ripgrep-*/out/{rg.bash-completion,rg.fish,_rg.ps1} \
"$td/$name/complete/"
cp complete/_rg "$td/$name/complete/"
pushd $td pushd $td
tar czf "$out_dir/$name.tar.gz" * tar czf "$out_dir/$name.tar.gz" *

View File

@@ -28,6 +28,8 @@ run_test_suite() {
cargo build --target $TARGET --verbose --manifest-path termcolor/Cargo.toml cargo build --target $TARGET --verbose --manifest-path termcolor/Cargo.toml
cargo test --target $TARGET --verbose --manifest-path termcolor/Cargo.toml cargo test --target $TARGET --verbose --manifest-path termcolor/Cargo.toml
"$( dirname "${0}" )/test_complete.sh"
# sanity check the file type # sanity check the file type
file target/$TARGET/debug/rg file target/$TARGET/debug/rg
} }

94
ci/test_complete.sh Executable file
View File

@@ -0,0 +1,94 @@
#!/usr/bin/env zsh
##
# Compares options in `rg --help` output to options in zsh completion function
emulate -R zsh
setopt extended_glob
setopt no_function_argzero
setopt no_unset
get_comp_args() {
setopt local_options unset
# Our completion function recognises a special variable which tells it to
# dump the _arguments specs and then just return. But do this in a sub-shell
# anyway to avoid any weirdness
( _RG_COMPLETE_LIST_ARGS=1 source $1 )
return $?
}
main() {
local diff
local rg="${${0:a}:h}/../target/${TARGET:-}/release/rg"
local _rg="${${0:a}:h}/../complete/_rg"
local -a help_args comp_args
[[ -e $rg ]] || rg=${rg/%\/release\/rg/\/debug\/rg}
[[ -e $rg ]] || {
printf >&2 'File not found: %s\n' $rg
return 1
}
[[ -e $_rg ]] || {
printf >&2 'File not found: %s\n' $_rg
return 1
}
printf 'Comparing options:\n-%s\n+%s\n' $rg $_rg
# 'Parse' options out of the `--help` output. To prevent false positives we
# only look at lines where the first non-white-space character is `-`
help_args=( ${(f)"$(
$rg --help |
$rg -- '^\s*-' |
$rg -io -- '[\t ,](-[a-z0-9]|--[a-z0-9-]+)\b' |
tr -d '\t ,' |
sort -u
)"} )
# 'Parse' options out of the completion function
comp_args=( ${(f)"$( get_comp_args $_rg )"} )
comp_args=( ${comp_args#\(*\)} ) # Strip excluded options
comp_args=( ${comp_args#\*} ) # Strip repetition indicator
comp_args=( ${comp_args%%-[:[]*} ) # Strip everything after -optname-
comp_args=( ${comp_args%%[:+=[]*} ) # Strip everything after other optspecs
comp_args=( ${comp_args##[^-]*} ) # Remove non-options
# This probably isn't necessary, but we should ensure the same order
comp_args=( ${(f)"$( printf '%s\n' $comp_args | sort -u )"} )
(( $#help_args )) || {
printf >&2 'Failed to get help_args\n'
return 1
}
(( $#comp_args )) || {
printf >&2 'Failed to get comp_args\n'
return 1
}
diff="$(
if diff --help 2>&1 | grep -qF -- '--label'; then
diff -U2 \
--label '`rg --help`' \
--label '`_rg`' \
=( printf '%s\n' $help_args ) =( printf '%s\n' $comp_args )
else
diff -U2 \
-L '`rg --help`' \
-L '`_rg`' \
=( printf '%s\n' $help_args ) =( printf '%s\n' $comp_args )
fi
)"
(( $#diff )) && {
printf >&2 '%s\n' 'zsh completion options differ from `--help` options:'
printf >&2 '%s\n' $diff
return 1
}
printf 'OK\n'
return 0
}
main "${@}"

255
complete/_rg Normal file
View File

@@ -0,0 +1,255 @@
#compdef rg
##
# zsh completion function for ripgrep
#
# Run ci/test_complete.sh after building to ensure that the options supported by
# this function stay in synch with the `rg` binary.
#
# @see https://github.com/zsh-users/zsh/blob/master/Etc/completion-style-guide
#
# Based on code from the zsh-users project — see copyright notice below.
_rg() {
local state_descr ret curcontext="${curcontext:-}"
local -a context line state
local -A opt_args val_args
local -a rg_args
# Sort by long option name to match `rg --help`
rg_args=(
'(-A -C --after-context --context)'{-A+,--after-context=}'[specify lines to show after each match]:number of lines'
'(-B -C --before-context --context)'{-B+,--before-context=}'[specify lines to show before each match]:number of lines'
'(-i -s -S --ignore-case --case-sensitive --smart-case)'{-s,--case-sensitive}'[search case-sensitively]'
'--color=[specify when to use colors in output]:when:( never auto always ansi )'
'*--colors=[specify color settings and styles]: :->colorspec'
'--column[show column numbers]'
'(-A -B -C --after-context --before-context --context)'{-C+,--context=}'[specify lines to show before and after each match]:number of lines'
'--context-separator=[specify string used to separate non-continuous context lines in output]:separator'
'(-c --count)'{-c,--count}'[only show count of matches for each file]'
'--debug[show debug messages]'
'--dfa-size-limit=[specify upper size limit of generated DFA]:DFA size'
'(-E --encoding)'{-E+,--encoding=}'[specify text encoding of files to search]: :_rg_encodings'
'*'{-f+,--file=}'[specify file containing patterns to search for]:file:_files'
"(1)--files[show each file that would be searched (but don't search)]"
'(-l --files-with-matches --files-without-match)'{-l,--files-with-matches}'[only show names of files with matches]'
'(-l --files-with-matches --files-without-match)--files-without-match[only show names of files without matches]'
'(-F --fixed-strings)'{-F,--fixed-strings}'[treat pattern as literal string instead of regular expression]'
'(-L --follow)'{-L,--follow}'[follow symlinks]'
'*'{-g+,--glob=}'[include or exclude files for searching that match the specified glob]:glob'
'(: -)'{-h,--help}'[display help information]'
'(-p --no-heading --pretty --vimgrep)--heading[show matches grouped by file name]'
'--hidden[search hidden files and directories]'
'*--iglob=[include or exclude files for searching that match the specified case-insensitive glob]:glob'
'(-i -s -S --case-sensitive --ignore-case --smart-case)'{-i,--ignore-case}'[search case-insensitively]'
'--ignore-file=[specify additional ignore file]:file:_files'
'(-v --invert-match)'{-v,--invert-match}'[invert matching]'
'(-n -N --line-number --no-line-number)'{-n,--line-number}'[show line numbers]'
'(-w -x --line-regexp --word-regexp)'{-x,--line-regexp}'[only show matches surrounded by line boundaries]'
'(-M --max-columns)'{-M+,--max-columns=}'[specify max length of lines to print]:number of bytes'
'(-m --max-count)'{-m+,--max-count=}'[specify max number of matches per file]:number of matches'
'--max-filesize=[specify size above which files should be ignored]:file size'
'--maxdepth=[specify max number of directories to descend]:number of directories'
'(--mmap --no-mmap)--mmap[search using memory maps when possible]'
'(-H --with-filename --no-filename)--no-filename[suppress all file names]'
"(-p --heading --pretty --vimgrep)--no-heading[don't group matches by file name]"
"(--no-ignore-parent)--no-ignore[don't respect ignore files]"
"--no-ignore-parent[don't respect ignore files in parent directories]"
"--no-ignore-vcs[don't respect version control ignore files]"
'(-n -N --line-number --no-line-number)'{-N,--no-line-number}'[suppress line numbers]'
'--no-messages[suppress all error messages]'
"(--mmap --no-mmap)--no-mmap[don't search using memory maps]"
'(-0 --null)'{-0,--null}'[print NUL byte after file names]'
'(-o --only-matching -r --replace)'{-o,--only-matching}'[show only matching part of each line]'
'--path-separator=[specify path separator to use when printing file names]:separator'
'(-p --heading --no-heading --pretty --vimgrep)'{-p,--pretty}'[alias for --color=always --heading -n]'
'(-q --quiet)'{-q,--quiet}'[suppress normal output]'
'--regex-size-limit=[specify upper size limit of compiled regex]:regex size'
'(1 -f --file)*'{-e+,--regexp=}'[specify pattern]:pattern'
'(-o --only-matching -r --replace)'{-r+,--replace=}'[specify string used to replace matches]:replace string'
'(-i -s -S --ignore-case --case-sensitive --smart-case)'{-S,--smart-case}'[search case-insensitively if the pattern is all lowercase]'
'(-j --threads)--sort-files[sort results by file path (disables parallelism)]'
'(-a --text)'{-a,--text}'[search binary files as if they were text]'
'(-j --sort-files --threads)'{-j+,--threads=}'[specify approximate number of threads to use]:number of threads'
'*'{-t+,--type=}'[only search files matching specified type]: :_rg_types'
'*--type-add=[add new glob for file type]: :->typespec'
'*--type-clear=[clear globs previously defined for specified file type]: :_rg_types'
# This should actually be exclusive with everything but other type options
'(:)--type-list[show all supported file types and their associated globs]'
'*'{-T+,--type-not=}"[don't search files matching specified type]: :_rg_types"
'*'{-u,--unrestricted}'[reduce level of "smart" searching]'
'(: -)'{-V,--version}'[display version information]'
'(-p --heading --no-heading --pretty)--vimgrep[show results in vim-compatible format]'
'(-H --no-filename --with-filename)'{-H,--with-filename}'[prefix each match with name of file that contains it]'
'(-w -x --line-regexp --word-regexp)'{-w,--word-regexp}'[only show matches surrounded by word boundaries]'
'(-e -f --file --files --regexp --type-list)1: :_rg_pattern'
'(--type-list)*:file:_files'
)
[[ ${_RG_COMPLETE_LIST_ARGS:-} == (1|t*|y*) ]] && {
printf '%s\n' "${rg_args[@]}"
return 0
}
_arguments -s -S : "${rg_args[@]}" && return 0
while (( $#state )); do
case "${state[1]}" in
colorspec)
# @todo I don't like this because it allows you to do weird things like
# `line:line:bg:`. Also, i would like the `compadd -q` behaviour
[[ -prefix *:none: ]] && return 1
[[ -prefix *:*:*:* ]] && return 1
_values -S ':' 'color/style type' \
'column[specify coloring for column numbers]: :->attribute' \
'line[specify coloring for line numbers]: :->attribute' \
'match[specify coloring for match text]: :->attribute' \
'path[specify color for file names]: :->attribute' && return 0
[[ "${state}" == 'attribute' ]] &&
_values -S ':' 'color/style attribute' \
'none[clear color/style for type]' \
'bg[specify background color]: :->color' \
'fg[specify foreground color]: :->color' \
'style[specify text style]: :->style' && return 0
[[ "${state}" == 'color' ]] &&
_values -S ':' 'color value' \
black blue green red cyan magenta yellow white && return 0
[[ "${state}" == 'style' ]] &&
_values -S ':' 'style value' \
bold nobold intense nointense && return 0
;;
typespec)
if compset -P '[^:]##:include:'; then
_sequence -s ',' _rg_types && return 0
# @todo This bit in particular could be better, but it's a little
# complex, and attempting to solve it seems to run us up against a crash
# bug — zsh # 40362
elif compset -P '[^:]##:'; then
_message 'glob or include directive' && return 1
elif [[ ! -prefix *:* ]]; then
_rg_types -qS ':' && return 0
fi
;;
esac
shift state
done
return 1
}
# zsh 5.1 refuses to complete options if a 'match-less' operand like our pattern
# could be 'completed' instead. We can use _guard() to avoid this problem, but
# it introduces another one: zsh won't print the message if we try to complete
# the pattern after having passed `--`. To work around *that* problem, we can
# use this function to bypass the _guard() when `--` is on the command line.
# This is inaccurate (it'd get confused by e.g. `rg -e --`), but zsh's handling
# of `--` isn't accurate anyway
_rg_pattern() {
if (( ${words[(I)--]} )); then
_message 'pattern'
else
_guard '^-*' 'pattern'
fi
}
# Complete encodings
_rg_encodings() {
local -a expl
local -aU _encodings
# This is impossible to read, but these encodings rarely if ever change, so it
# probably doesn't matter. They are derived from the list given here:
# https://encoding.spec.whatwg.org/#concept-encoding-get
_encodings=(
{{,us-}ascii,arabic,chinese,cyrillic,greek{,8},hebrew,korean}
logical visual mac {,cs}macintosh x-mac-{cyrillic,roman,ukrainian}
866 ibm{819,866} csibm866
big5{,-hkscs} {cn-,cs}big5 x-x-big5
cp{819,866,125{0..8}} x-cp125{0..8}
csiso2022{jp,kr} csiso8859{6,8}{e,i}
csisolatin{{1..6},9} csisolatin{arabic,cyrillic,greek,hebrew}
ecma-{114,118} asmo-708 elot_928 sun_eu_greek
euc-{jp,kr} x-euc-jp cseuckr cseucpkdfmtjapanese
{,x-}gbk csiso58gb231280 gb18030 {,cs}gb2312 gb_2312{,-80} hz-gb-2312
iso-2022-{cn,cn-ext,jp,kr}
iso8859{,-}{{1..11},13,14,15}
iso-8859-{{1..11},{6,8}-{e,i},13,14,15,16} iso_8859-{{1..9},15}
iso_8859-{1,2,6,7}:1987 iso_8859-{3,4,5,8}:1988 iso_8859-9:1989
iso-ir-{58,100,101,109,110,126,127,138,144,148,149,157}
koi{,8,8-r,8-ru,8-u,8_r} cskoi8r
ks_c_5601-{1987,1989} ksc{,_}5691 csksc56011987
latin{1..6} l{{1..6},9}
shift{-,_}jis csshiftjis {,x-}sjis ms_kanji ms932
utf{,-}8 utf-16{,be,le} unicode-1-1-utf-8
windows-{31j,874,949,125{0..8}} dos-874 tis-620 ansi_x3.4-1968
x-user-defined auto
)
_wanted rg-encodings expl 'encoding' compadd -a "${@}" - _encodings
}
# Complete file types
_rg_types() {
local -a expl
local -aU _types
_types=( ${${(f)"$( _call_program rg-types rg --type-list )"}%%:*} )
_wanted rg-types expl 'file type' compadd -a "${@}" - _types
}
_rg "${@}"
# ------------------------------------------------------------------------------
# Copyright (c) 2011 Github zsh-users - http://github.com/zsh-users
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the zsh-users nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL ZSH-USERS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ------------------------------------------------------------------------------
# Description
# -----------
#
# Completion script for ripgrep
#
# ------------------------------------------------------------------------------
# Authors
# -------
#
# * arcizan <ghostrevery@gmail.com>
# * MaskRay <i@maskray.me>
#
# ------------------------------------------------------------------------------
# Local Variables:
# mode: shell-script
# coding: utf-8-unix
# indent-tabs-mode: nil
# sh-indentation: 2
# sh-basic-offset: 2
# End:
# vim: ft=zsh sw=2 ts=2 et

View File

@@ -1,4 +1,5 @@
#!/bin/sh #!/bin/sh -e
pandoc -s -t man rg.1.md -o rg.1 pandoc -s -t man rg.1.md -o rg.1
sed -i 's/\.TH.*/.TH "rg" "1"/g' rg.1 sed -i.bak 's/\.TH.*/.TH "rg" "1"/g' rg.1
rm -f rg.1.bak # BSD `sed` requires the creation of a back-up file

112
doc/rg.1
View File

@@ -7,12 +7,12 @@
rg \- recursively search current directory for lines matching a pattern rg \- recursively search current directory for lines matching a pattern
.SH SYNOPSIS .SH SYNOPSIS
.PP .PP
rg [\f[I]options\f[]] <\f[I]pattern\f[]> [\f[I]<\f[]path\f[I]> ...\f[]] rg [\f[I]options\f[]] \f[I]PATTERN\f[] [\f[I]path\f[] ...]
.PP .PP
rg [\f[I]options\f[]] (\-e PATTERN | \-f FILE) ... rg [\f[I]options\f[]] [\-e \f[I]PATTERN\f[] ...] [\-f \f[I]FILE\f[] ...]
[\f[I]<\f[]path\f[I]> ...\f[]] [\f[I]path\f[] ...]
.PP .PP
rg [\f[I]options\f[]] \-\-files [\f[I]<\f[]path\f[I]> ...\f[]] rg [\f[I]options\f[]] \-\-files [\f[I]path\f[] ...]
.PP .PP
rg [\f[I]options\f[]] \-\-type\-list rg [\f[I]options\f[]] \-\-type\-list
.PP .PP
@@ -29,6 +29,10 @@ time searching.
Because of this, features like backreferences and arbitrary lookaround Because of this, features like backreferences and arbitrary lookaround
are not supported. are not supported.
.PP .PP
Note that ripgrep may abort unexpectedly when using default settings if
it searches a file that is simultaneously truncated.
This behavior can be avoided by passing the \-\-no\-mmap flag.
.PP
Project home page: https://github.com/BurntSushi/ripgrep Project home page: https://github.com/BurntSushi/ripgrep
.SH COMMON OPTIONS .SH COMMON OPTIONS
.TP .TP
@@ -43,9 +47,12 @@ Only show count of line matches for each file.
.RE .RE
.TP .TP
.B \-\-color \f[I]WHEN\f[] .B \-\-color \f[I]WHEN\f[]
Whether to use coloring in match. Whether to use color in the output.
Valid values are never, always or auto. Valid values are never, auto, always or ansi.
[default: auto] The default is auto.
When always is used, coloring is attempted based on your environment.
When ansi is used, coloring is forcefully done using ANSI escape color
codes.
.RS .RS
.RE .RE
.TP .TP
@@ -71,22 +78,23 @@ Multiple glob flags may be used.
Globbing rules match .gitignore globs. Globbing rules match .gitignore globs.
Precede a glob with a \[aq]!\[aq] to exclude it. Precede a glob with a \[aq]!\[aq] to exclude it.
.RS .RS
.RE
.PP .PP
The \-\-glob flag subsumes the functionality of both the \-\-include and The \-\-glob flag subsumes the functionality of both the \-\-include and
\-\-exclude flags commonly found in other tools. \-\-exclude flags commonly found in other tools.
.PP
Values given to \-g must be quoted or your shell will expand them and
result in unexpected behavior.
.PP
Combine with the \-\-files flag to return matched filenames (i.e., to
replicate ack/ag\[aq]s \-g flag).
For example:
.IP .IP
.nf .nf
\f[C] \f[C]
Values\ given\ to\ \-g\ must\ be\ quoted\ or\ your\ shell\ will\ expand\ them\ and\ result rg\ \-g\ \[aq]*.foo\[aq]\ \-\-files
in\ unexpected\ behavior.
Combine\ with\ the\ \-\-files\ flag\ to\ return\ matched\ filenames
(i.e.,\ to\ replicate\ ack/ag\[aq]s\ \-g\ flag).
For\ example:\ rg\ \-g\ \[aq]\\<glob\\>\[aq]\ \-\-files
\f[] \f[]
.fi .fi
.RE
.TP .TP
.B \-h, \-\-help .B \-h, \-\-help
Show this usage message. Show this usage message.
@@ -134,14 +142,15 @@ Reduce the level of \[aq]smart\[aq] searching.
A single \-u doesn\[aq]t respect .gitignore (etc.) files. A single \-u doesn\[aq]t respect .gitignore (etc.) files.
Two \-u flags will search hidden files and directories. Two \-u flags will search hidden files and directories.
Three \-u flags will search binary files. Three \-u flags will search binary files.
\-uu is equivalent to grep \-r, and \-uuu is equivalent to grep \-a \-r. \-uu is equivalent to \f[C]grep\ \-r\f[], and \-uuu is equivalent to
\f[C]grep\ \-a\ \-r\f[].
.RS .RS
.PP .PP
Note that the \-u flags are convenient aliases for other combinations of Note that the \-u flags are convenient aliases for other combinations of
flags. flags.
\-u aliases \[aq]\-\-no\-ignore\[aq]. \-u aliases \-\-no\-ignore.
\-uu aliases \[aq]\-\-no\-ignore \-\-hidden\[aq]. \-uu aliases \-\-no\-ignore \-\-hidden.
\-uuu aliases \[aq]\-\-no\-ignore \-\-hidden \-\-text\[aq]. \-uuu aliases \-\-no\-ignore \-\-hidden \-\-text.
.RE .RE
.TP .TP
.B \-v, \-\-invert\-match .B \-v, \-\-invert\-match
@@ -154,6 +163,12 @@ Only show matches surrounded by word boundaries.
This is equivalent to putting \\b before and after the search pattern. This is equivalent to putting \\b before and after the search pattern.
.RS .RS
.RE .RE
.TP
.B \-x, \-\-line\-regexp
Only show matches surrounded by line boundaries.
This is equivalent to putting ^...$ around the search pattern.
.RS
.RE
.SH LESS COMMON OPTIONS .SH LESS COMMON OPTIONS
.TP .TP
.B \-A, \-\-after\-context \f[I]NUM\f[] .B \-A, \-\-after\-context \f[I]NUM\f[]
@@ -188,9 +203,12 @@ A special format, {type}:none, will clear all color settings for {type}.
.PP .PP
For example, the following command will change the match color to For example, the following command will change the match color to
magenta and the background color for line numbers to yellow: magenta and the background color for line numbers to yellow:
.PP .IP
rg \-\-colors \[aq]match:fg:magenta\[aq] \-\-colors .nf
\[aq]line:bg:yellow\[aq] foo. \f[C]
rg\ \-\-colors\ \[aq]match:fg:magenta\[aq]\ \-\-colors\ \[aq]line:bg:yellow\[aq]\ foo.
\f[]
.fi
.RE .RE
.TP .TP
.B \-\-column .B \-\-column
@@ -223,7 +241,7 @@ https://encoding.spec.whatwg.org/#concept\-encoding\-get
.RS .RS
.RE .RE
.TP .TP
.B \-f, \-\-file FILE ... .B \-f, \-\-file \f[I]FILE\f[] ...
Search for patterns from the given file, with one pattern per line. Search for patterns from the given file, with one pattern per line.
When this flag is used or multiple times or in combination with the When this flag is used or multiple times or in combination with the
\-e/\-\-regexp flag, then all patterns provided are searched. \-e/\-\-regexp flag, then all patterns provided are searched.
@@ -237,8 +255,12 @@ Print each file that would be searched (but don\[aq]t search).
.RS .RS
.PP .PP
Combine with the \-g flag to return matched paths, for example: Combine with the \-g flag to return matched paths, for example:
.PP .IP
rg \-g \[aq]<glob>\[aq] \-\-files .nf
\f[C]
rg\ \-g\ \[aq]*.foo\[aq]\ \-\-files
\f[]
.fi
.RE .RE
.TP .TP
.B \-l, \-\-files\-with\-matches .B \-l, \-\-files\-with\-matches
@@ -274,7 +296,7 @@ This is the default mode at a tty.
Don\[aq]t group matches by each file. Don\[aq]t group matches by each file.
If \-H/\-\-with\-filename is enabled, then file names will be shown for If \-H/\-\-with\-filename is enabled, then file names will be shown for
every line matched. every line matched.
This is the default more when not at a tty. This is the default mode when not at a tty.
.RS .RS
.RE .RE
.TP .TP
@@ -284,7 +306,18 @@ Search hidden directories and files.
.RS .RS
.RE .RE
.TP .TP
.B \-\-ignore\-file FILE ... .B \-\-iglob \f[I]GLOB\f[] ...
Include or exclude files/directories case insensitively.
This always overrides any other ignore logic if there is a conflict, but
is otherwise applied in addition to ignore files (e.g., .gitignore or
\&.ignore).
Multiple glob flags may be used.
Globbing rules match .gitignore globs.
Precede a glob with a \[aq]!\[aq] to exclude it.
.RS
.RE
.TP
.B \-\-ignore\-file \f[I]FILE\f[] ...
Specify additional ignore files for filtering file paths. Specify additional ignore files for filtering file paths.
Ignore files should be in the gitignore format and are matched relative Ignore files should be in the gitignore format and are matched relative
to the current working directory. to the current working directory.
@@ -387,7 +420,7 @@ A path separator is limited to a single byte.
.RE .RE
.TP .TP
.B \-p, \-\-pretty .B \-p, \-\-pretty
Alias for \-\-color=always \-\-heading \-n. Alias for \-\-color=always \-\-heading \-\-line\-number.
.RS .RS
.RE .RE
.TP .TP
@@ -446,9 +479,21 @@ Show the version number of ripgrep and exit.
.B \-\-vimgrep .B \-\-vimgrep
Show results with every match on its own line, including line numbers Show results with every match on its own line, including line numbers
and column numbers. and column numbers.
(With this option, a line with more than one match of the regex will be With this option, a line with more than one match will be printed more
printed more than once.) than once.
.RS .RS
.PP
Recommended .vimrc configuration:
.IP
.nf
\f[C]
\ \ set\ grepprg=rg\\\ \-\-vimgrep
\ \ set\ grepformat^=%f:%l:%c:%m
\f[]
.fi
.PP
Use :grep to grep for something, then :cn and :cp to navigate through
the matches.
.RE .RE
.SH FILE TYPE MANAGEMENT OPTIONS .SH FILE TYPE MANAGEMENT OPTIONS
.TP .TP
@@ -465,11 +510,12 @@ Unless \-\-type\-clear is used, globs are added to any existing globs
inside of ripgrep. inside of ripgrep.
Note that this must be passed to every invocation of rg. Note that this must be passed to every invocation of rg.
Type settings are NOT persisted. Type settings are NOT persisted.
Example:
.RS .RS
.IP .IP
.nf .nf
\f[C] \f[C]
\ \ Example:\ `rg\ \-\-type\-add\ \[aq]foo:*.foo\[aq]\ \-tfoo\ PATTERN` \ \ rg\ \-\-type\-add\ \[aq]foo:*.foo\[aq]\ \-tfoo\ PATTERN
\f[] \f[]
.fi .fi
.PP .PP
@@ -483,7 +529,7 @@ Markdown files, one can use:
.IP .IP
.nf .nf
\f[C] \f[C]
\ \ `\-\-type\-add\ \[aq]src:include:cpp,py,md\[aq]` \ \ \-\-type\-add\ \[aq]src:include:cpp,py,md\[aq]
\f[] \f[]
.fi .fi
.PP .PP
@@ -492,7 +538,7 @@ Additional glob rules can still be added to the src type by using the
.IP .IP
.nf .nf
\f[C] \f[C]
\ \ `\-\-type\-add\ \[aq]src:include:cpp,py,md\[aq]\ \-\-type\-add\ \[aq]src:*.foo\[aq]` \ \ \-\-type\-add\ \[aq]src:include:cpp,py,md\[aq]\ \-\-type\-add\ \[aq]src:*.foo\[aq]
\f[] \f[]
.fi .fi
.PP .PP

View File

@@ -4,11 +4,11 @@ rg - recursively search current directory for lines matching a pattern
# SYNOPSIS # SYNOPSIS
rg [*options*] <*pattern*> [*<*path*> ...*] rg [*options*] *PATTERN* [*path* ...]
rg [*options*] (-e PATTERN | -f FILE) ... [*<*path*> ...*] rg [*options*] [-e *PATTERN* ...] [-f *FILE* ...] [*path* ...]
rg [*options*] --files [*<*path*> ...*] rg [*options*] --files [*path* ...]
rg [*options*] --type-list rg [*options*] --type-list
@@ -25,6 +25,10 @@ ripgrep's regex engine uses finite automata and guarantees linear time
searching. Because of this, features like backreferences and arbitrary searching. Because of this, features like backreferences and arbitrary
lookaround are not supported. lookaround are not supported.
Note that ripgrep may abort unexpectedly when using default settings if it
searches a file that is simultaneously truncated. This behavior can be avoided
by passing the --no-mmap flag.
Project home page: https://github.com/BurntSushi/ripgrep Project home page: https://github.com/BurntSushi/ripgrep
# COMMON OPTIONS # COMMON OPTIONS
@@ -36,8 +40,10 @@ Project home page: https://github.com/BurntSushi/ripgrep
: Only show count of line matches for each file. : Only show count of line matches for each file.
--color *WHEN* --color *WHEN*
: Whether to use coloring in match. Valid values are never, always or auto. : Whether to use color in the output. Valid values are never, auto, always or
[default: auto] ansi. The default is auto. When always is used, coloring is attempted based
on your environment. When ansi is used, coloring is forcefully done using
ANSI escape color codes.
-e, --regexp *PATTERN* ... -e, --regexp *PATTERN* ...
: Use PATTERN to search. This option can be provided multiple times, where all : Use PATTERN to search. This option can be provided multiple times, where all
@@ -54,16 +60,16 @@ Project home page: https://github.com/BurntSushi/ripgrep
glob flags may be used. Globbing rules match .gitignore globs. Precede a glob flags may be used. Globbing rules match .gitignore globs. Precede a
glob with a '!' to exclude it. glob with a '!' to exclude it.
The --glob flag subsumes the functionality of both the --include and The --glob flag subsumes the functionality of both the --include and
--exclude flags commonly found in other tools. --exclude flags commonly found in other tools.
Values given to -g must be quoted or your shell will expand them and result Values given to -g must be quoted or your shell will expand them and result
in unexpected behavior. in unexpected behavior.
Combine with the --files flag to return matched filenames Combine with the --files flag to return matched filenames
(i.e., to replicate ack/ag's -g flag). (i.e., to replicate ack/ag's -g flag). For example:
For example: rg -g '\<glob\>' --files rg -g '*.foo' --files
-h, --help -h, --help
: Show this usage message. : Show this usage message.
@@ -91,12 +97,12 @@ Project home page: https://github.com/BurntSushi/ripgrep
-u, --unrestricted ... -u, --unrestricted ...
: Reduce the level of 'smart' searching. A single -u doesn't respect .gitignore : Reduce the level of 'smart' searching. A single -u doesn't respect .gitignore
(etc.) files. Two -u flags will search hidden files and directories. Three (etc.) files. Two -u flags will search hidden files and directories. Three
-u flags will search binary files. -uu is equivalent to grep -r, and -uuu is -u flags will search binary files. -uu is equivalent to `grep -r`, and -uuu
equivalent to grep -a -r. is equivalent to `grep -a -r`.
Note that the -u flags are convenient aliases for other combinations of Note that the -u flags are convenient aliases for other combinations of
flags. -u aliases '--no-ignore'. -uu aliases '--no-ignore --hidden'. flags. -u aliases --no-ignore. -uu aliases --no-ignore --hidden.
-uuu aliases '--no-ignore --hidden --text'. -uuu aliases --no-ignore --hidden --text.
-v, --invert-match -v, --invert-match
: Invert matching. : Invert matching.
@@ -105,6 +111,10 @@ Project home page: https://github.com/BurntSushi/ripgrep
: Only show matches surrounded by word boundaries. This is equivalent to : Only show matches surrounded by word boundaries. This is equivalent to
putting \\b before and after the search pattern. putting \\b before and after the search pattern.
-x, --line-regexp
: Only show matches surrounded by line boundaries. This is equivalent to
putting ^...$ around the search pattern.
# LESS COMMON OPTIONS # LESS COMMON OPTIONS
-A, --after-context *NUM* -A, --after-context *NUM*
@@ -130,7 +140,7 @@ Project home page: https://github.com/BurntSushi/ripgrep
For example, the following command will change the match color to magenta For example, the following command will change the match color to magenta
and the background color for line numbers to yellow: and the background color for line numbers to yellow:
rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo. rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo.
--column --column
: Show column numbers (1 based) in output. This only shows the column : Show column numbers (1 based) in output. This only shows the column
@@ -152,7 +162,7 @@ Project home page: https://github.com/BurntSushi/ripgrep
Other supported values can be found in the list of labels here: Other supported values can be found in the list of labels here:
https://encoding.spec.whatwg.org/#concept-encoding-get https://encoding.spec.whatwg.org/#concept-encoding-get
-f, --file FILE ... -f, --file *FILE* ...
: Search for patterns from the given file, with one pattern per line. When this : Search for patterns from the given file, with one pattern per line. When this
flag is used or multiple times or in combination with the -e/--regexp flag, flag is used or multiple times or in combination with the -e/--regexp flag,
then all patterns provided are searched. Empty pattern lines will match all then all patterns provided are searched. Empty pattern lines will match all
@@ -163,7 +173,7 @@ Project home page: https://github.com/BurntSushi/ripgrep
Combine with the -g flag to return matched paths, for example: Combine with the -g flag to return matched paths, for example:
rg -g '\<glob\>' --files rg -g '*.foo' --files
-l, --files-with-matches -l, --files-with-matches
: Only show path of each file with matches. : Only show path of each file with matches.
@@ -185,14 +195,21 @@ Project home page: https://github.com/BurntSushi/ripgrep
--no-heading --no-heading
: Don't group matches by each file. If -H/--with-filename is enabled, then : Don't group matches by each file. If -H/--with-filename is enabled, then
file names will be shown for every line matched. This is the default more file names will be shown for every line matched. This is the default mode
when not at a tty. when not at a tty.
--hidden --hidden
: Search hidden directories and files. (Hidden directories and files are : Search hidden directories and files. (Hidden directories and files are
skipped by default.) skipped by default.)
--ignore-file FILE ... --iglob *GLOB* ...
: Include or exclude files/directories case insensitively. This always
overrides any other ignore logic if there is a conflict, but is otherwise
applied in addition to ignore files (e.g., .gitignore or .ignore). Multiple
glob flags may be used. Globbing rules match .gitignore globs. Precede a
glob with a '!' to exclude it.
--ignore-file *FILE* ...
: Specify additional ignore files for filtering file paths. : Specify additional ignore files for filtering file paths.
Ignore files should be in the gitignore format and are matched Ignore files should be in the gitignore format and are matched
relative to the current working directory. These ignore files relative to the current working directory. These ignore files
@@ -260,7 +277,7 @@ Project home page: https://github.com/BurntSushi/ripgrep
cygwin). A path separator is limited to a single byte. cygwin). A path separator is limited to a single byte.
-p, --pretty -p, --pretty
: Alias for --color=always --heading -n. : Alias for --color=always --heading --line-number.
-r, --replace *ARG* -r, --replace *ARG*
: Replace every match with the string given when printing search results. : Replace every match with the string given when printing search results.
@@ -295,9 +312,17 @@ Project home page: https://github.com/BurntSushi/ripgrep
: Show the version number of ripgrep and exit. : Show the version number of ripgrep and exit.
--vimgrep --vimgrep
: Show results with every match on its own line, including line : Show results with every match on its own line, including
numbers and column numbers. (With this option, a line with more line numbers and column numbers. With this option, a line with
than one match of the regex will be printed more than once.) more than one match will be printed more than once.
Recommended .vimrc configuration:
set grepprg=rg\ --vimgrep
set grepformat^=%f:%l:%c:%m
Use :grep to grep for something, then :cn and :cp to navigate through the
matches.
# FILE TYPE MANAGEMENT OPTIONS # FILE TYPE MANAGEMENT OPTIONS
@@ -309,9 +334,9 @@ Project home page: https://github.com/BurntSushi/ripgrep
at a time. Multiple --type-add flags can be provided. Unless --type-clear at a time. Multiple --type-add flags can be provided. Unless --type-clear
is used, globs are added to any existing globs inside of ripgrep. Note that is used, globs are added to any existing globs inside of ripgrep. Note that
this must be passed to every invocation of rg. Type settings are NOT this must be passed to every invocation of rg. Type settings are NOT
persisted. persisted. Example:
Example: `rg --type-add 'foo:*.foo' -tfoo PATTERN` rg --type-add 'foo:*.foo' -tfoo PATTERN
--type-add can also be used to include rules from other types --type-add can also be used to include rules from other types
with the special include directive. The include directive with the special include directive. The include directive
@@ -321,12 +346,12 @@ Project home page: https://github.com/BurntSushi/ripgrep
type called src that matches C++, Python and Markdown files, one type called src that matches C++, Python and Markdown files, one
can use: can use:
`--type-add 'src:include:cpp,py,md'` --type-add 'src:include:cpp,py,md'
Additional glob rules can still be added to the src type by Additional glob rules can still be added to the src type by
using the --type-add flag again: using the --type-add flag again:
`--type-add 'src:include:cpp,py,md' --type-add 'src:*.foo'` --type-add 'src:include:cpp,py,md' --type-add 'src:*.foo'
Note that type names must consist only of Unicode letters or Note that type names must consist only of Unicode letters or
numbers. Punctuation characters are not allowed. numbers. Punctuation characters are not allowed.

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "globset" name = "globset"
version = "0.1.4" #:version version = "0.2.0" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"] authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """ description = """
Cross platform single glob and glob set matching. Glob set matching is the Cross platform single glob and glob set matching. Glob set matching is the

View File

@@ -9,7 +9,7 @@ use std::str;
use regex; use regex;
use regex::bytes::Regex; use regex::bytes::Regex;
use {Candidate, Error, new_regex}; use {Candidate, Error, ErrorKind, new_regex};
/// Describes a matching strategy for a particular pattern. /// Describes a matching strategy for a particular pattern.
/// ///
@@ -544,6 +544,7 @@ impl<'a> GlobBuilder<'a> {
/// Parses and builds the pattern. /// Parses and builds the pattern.
pub fn build(&self) -> Result<Glob, Error> { pub fn build(&self) -> Result<Glob, Error> {
let mut p = Parser { let mut p = Parser {
glob: &self.glob,
stack: vec![Tokens::default()], stack: vec![Tokens::default()],
chars: self.glob.chars().peekable(), chars: self.glob.chars().peekable(),
prev: None, prev: None,
@@ -551,9 +552,15 @@ impl<'a> GlobBuilder<'a> {
}; };
try!(p.parse()); try!(p.parse());
if p.stack.is_empty() { if p.stack.is_empty() {
Err(Error::UnopenedAlternates) Err(Error {
glob: Some(self.glob.to_string()),
kind: ErrorKind::UnopenedAlternates,
})
} else if p.stack.len() > 1 { } else if p.stack.len() > 1 {
Err(Error::UnclosedAlternates) Err(Error {
glob: Some(self.glob.to_string()),
kind: ErrorKind::UnclosedAlternates,
})
} else { } else {
let tokens = p.stack.pop().unwrap(); let tokens = p.stack.pop().unwrap();
Ok(Glob { Ok(Glob {
@@ -698,6 +705,7 @@ fn bytes_to_escaped_literal(bs: &[u8]) -> String {
} }
struct Parser<'a> { struct Parser<'a> {
glob: &'a str,
stack: Vec<Tokens>, stack: Vec<Tokens>,
chars: iter::Peekable<str::Chars<'a>>, chars: iter::Peekable<str::Chars<'a>>,
prev: Option<char>, prev: Option<char>,
@@ -705,6 +713,10 @@ struct Parser<'a> {
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
fn error(&self, kind: ErrorKind) -> Error {
Error { glob: Some(self.glob.to_string()), kind: kind }
}
fn parse(&mut self) -> Result<(), Error> { fn parse(&mut self) -> Result<(), Error> {
while let Some(c) = self.bump() { while let Some(c) = self.bump() {
match c { match c {
@@ -729,7 +741,7 @@ impl<'a> Parser<'a> {
fn push_alternate(&mut self) -> Result<(), Error> { fn push_alternate(&mut self) -> Result<(), Error> {
if self.stack.len() > 1 { if self.stack.len() > 1 {
return Err(Error::NestedAlternates); return Err(self.error(ErrorKind::NestedAlternates));
} }
Ok(self.stack.push(Tokens::default())) Ok(self.stack.push(Tokens::default()))
} }
@@ -743,22 +755,22 @@ impl<'a> Parser<'a> {
} }
fn push_token(&mut self, tok: Token) -> Result<(), Error> { fn push_token(&mut self, tok: Token) -> Result<(), Error> {
match self.stack.last_mut() { if let Some(ref mut pat) = self.stack.last_mut() {
None => Err(Error::UnopenedAlternates), return Ok(pat.push(tok));
Some(ref mut pat) => Ok(pat.push(tok)),
} }
Err(self.error(ErrorKind::UnopenedAlternates))
} }
fn pop_token(&mut self) -> Result<Token, Error> { fn pop_token(&mut self) -> Result<Token, Error> {
match self.stack.last_mut() { if let Some(ref mut pat) = self.stack.last_mut() {
None => Err(Error::UnopenedAlternates), return Ok(pat.pop().unwrap());
Some(ref mut pat) => Ok(pat.pop().unwrap()),
} }
Err(self.error(ErrorKind::UnopenedAlternates))
} }
fn have_tokens(&self) -> Result<bool, Error> { fn have_tokens(&self) -> Result<bool, Error> {
match self.stack.last() { match self.stack.last() {
None => Err(Error::UnopenedAlternates), None => Err(self.error(ErrorKind::UnopenedAlternates)),
Some(ref pat) => Ok(!pat.is_empty()), Some(ref pat) => Ok(!pat.is_empty()),
} }
} }
@@ -785,7 +797,7 @@ impl<'a> Parser<'a> {
try!(self.push_token(Token::RecursivePrefix)); try!(self.push_token(Token::RecursivePrefix));
let next = self.bump(); let next = self.bump();
if !next.map(is_separator).unwrap_or(true) { if !next.map(is_separator).unwrap_or(true) {
return Err(Error::InvalidRecursive); return Err(self.error(ErrorKind::InvalidRecursive));
} }
return Ok(()); return Ok(());
} }
@@ -793,7 +805,7 @@ impl<'a> Parser<'a> {
if !prev.map(is_separator).unwrap_or(false) { if !prev.map(is_separator).unwrap_or(false) {
if self.stack.len() <= 1 if self.stack.len() <= 1
|| (prev != Some(',') && prev != Some('{')) { || (prev != Some(',') && prev != Some('{')) {
return Err(Error::InvalidRecursive); return Err(self.error(ErrorKind::InvalidRecursive));
} }
} }
match self.chars.peek() { match self.chars.peek() {
@@ -808,18 +820,22 @@ impl<'a> Parser<'a> {
assert!(self.bump().map(is_separator).unwrap_or(false)); assert!(self.bump().map(is_separator).unwrap_or(false));
self.push_token(Token::RecursiveZeroOrMore) self.push_token(Token::RecursiveZeroOrMore)
} }
_ => Err(Error::InvalidRecursive), _ => Err(self.error(ErrorKind::InvalidRecursive)),
} }
} }
fn parse_class(&mut self) -> Result<(), Error> { fn parse_class(&mut self) -> Result<(), Error> {
fn add_to_last_range( fn add_to_last_range(
glob: &str,
r: &mut (char, char), r: &mut (char, char),
add: char, add: char,
) -> Result<(), Error> { ) -> Result<(), Error> {
r.1 = add; r.1 = add;
if r.1 < r.0 { if r.1 < r.0 {
Err(Error::InvalidRange(r.0, r.1)) Err(Error {
glob: Some(glob.to_string()),
kind: ErrorKind::InvalidRange(r.0, r.1),
})
} else { } else {
Ok(()) Ok(())
} }
@@ -837,7 +853,7 @@ impl<'a> Parser<'a> {
Some(c) => c, Some(c) => c,
// The only way to successfully break this loop is to observe // The only way to successfully break this loop is to observe
// a ']'. // a ']'.
None => return Err(Error::UnclosedClass), None => return Err(self.error(ErrorKind::UnclosedClass)),
}; };
match c { match c {
']' => { ']' => {
@@ -854,7 +870,7 @@ impl<'a> Parser<'a> {
// invariant: in_range is only set when there is // invariant: in_range is only set when there is
// already at least one character seen. // already at least one character seen.
let r = ranges.last_mut().unwrap(); let r = ranges.last_mut().unwrap();
try!(add_to_last_range(r, '-')); try!(add_to_last_range(&self.glob, r, '-'));
in_range = false; in_range = false;
} else { } else {
assert!(!ranges.is_empty()); assert!(!ranges.is_empty());
@@ -865,7 +881,8 @@ impl<'a> Parser<'a> {
if in_range { if in_range {
// invariant: in_range is only set when there is // invariant: in_range is only set when there is
// already at least one character seen. // already at least one character seen.
try!(add_to_last_range(ranges.last_mut().unwrap(), c)); try!(add_to_last_range(
&self.glob, ranges.last_mut().unwrap(), c));
} else { } else {
ranges.push((c, c)); ranges.push((c, c));
} }
@@ -909,7 +926,7 @@ fn ends_with(needle: &[u8], haystack: &[u8]) -> bool {
mod tests { mod tests {
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use {GlobSetBuilder, Error}; use {GlobSetBuilder, ErrorKind};
use super::{Glob, GlobBuilder, Token}; use super::{Glob, GlobBuilder, Token};
use super::Token::*; use super::Token::*;
@@ -934,7 +951,7 @@ mod tests {
#[test] #[test]
fn $name() { fn $name() {
let err = Glob::new($pat).unwrap_err(); let err = Glob::new($pat).unwrap_err();
assert_eq!($err, err); assert_eq!(&$err, err.kind());
} }
} }
} }
@@ -1057,19 +1074,19 @@ mod tests {
syntax!(cls18, "[!0-9a-z]", vec![rclassn(&[('0', '9'), ('a', 'z')])]); syntax!(cls18, "[!0-9a-z]", vec![rclassn(&[('0', '9'), ('a', 'z')])]);
syntax!(cls19, "[!a-z0-9]", vec![rclassn(&[('a', 'z'), ('0', '9')])]); syntax!(cls19, "[!a-z0-9]", vec![rclassn(&[('a', 'z'), ('0', '9')])]);
syntaxerr!(err_rseq1, "a**", Error::InvalidRecursive); syntaxerr!(err_rseq1, "a**", ErrorKind::InvalidRecursive);
syntaxerr!(err_rseq2, "**a", Error::InvalidRecursive); syntaxerr!(err_rseq2, "**a", ErrorKind::InvalidRecursive);
syntaxerr!(err_rseq3, "a**b", Error::InvalidRecursive); syntaxerr!(err_rseq3, "a**b", ErrorKind::InvalidRecursive);
syntaxerr!(err_rseq4, "***", Error::InvalidRecursive); syntaxerr!(err_rseq4, "***", ErrorKind::InvalidRecursive);
syntaxerr!(err_rseq5, "/a**", Error::InvalidRecursive); syntaxerr!(err_rseq5, "/a**", ErrorKind::InvalidRecursive);
syntaxerr!(err_rseq6, "/**a", Error::InvalidRecursive); syntaxerr!(err_rseq6, "/**a", ErrorKind::InvalidRecursive);
syntaxerr!(err_rseq7, "/a**b", Error::InvalidRecursive); syntaxerr!(err_rseq7, "/a**b", ErrorKind::InvalidRecursive);
syntaxerr!(err_unclosed1, "[", Error::UnclosedClass); syntaxerr!(err_unclosed1, "[", ErrorKind::UnclosedClass);
syntaxerr!(err_unclosed2, "[]", Error::UnclosedClass); syntaxerr!(err_unclosed2, "[]", ErrorKind::UnclosedClass);
syntaxerr!(err_unclosed3, "[!", Error::UnclosedClass); syntaxerr!(err_unclosed3, "[!", ErrorKind::UnclosedClass);
syntaxerr!(err_unclosed4, "[!]", Error::UnclosedClass); syntaxerr!(err_unclosed4, "[!]", ErrorKind::UnclosedClass);
syntaxerr!(err_range1, "[z-a]", Error::InvalidRange('z', 'a')); syntaxerr!(err_range1, "[z-a]", ErrorKind::InvalidRange('z', 'a'));
syntaxerr!(err_range2, "[z--]", Error::InvalidRange('z', '-')); syntaxerr!(err_range2, "[z--]", ErrorKind::InvalidRange('z', '-'));
const CASEI: Options = Options { const CASEI: Options = Options {
casei: true, casei: true,

View File

@@ -128,7 +128,16 @@ mod pathutil;
/// Represents an error that can occur when parsing a glob pattern. /// Represents an error that can occur when parsing a glob pattern.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum Error { pub struct Error {
/// The original glob provided by the caller.
glob: Option<String>,
/// The kind of error.
kind: ErrorKind,
}
/// The kind of error that can occur when parsing a glob pattern.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ErrorKind {
/// Occurs when a use of `**` is invalid. Namely, `**` can only appear /// Occurs when a use of `**` is invalid. Namely, `**` can only appear
/// adjacent to a path separator, or the beginning/end of a glob. /// adjacent to a path separator, or the beginning/end of a glob.
InvalidRecursive, InvalidRecursive,
@@ -150,45 +159,74 @@ pub enum Error {
} }
impl StdError for Error { impl StdError for Error {
fn description(&self) -> &str {
self.kind.description()
}
}
impl Error {
/// Return the glob that caused this error, if one exists.
pub fn glob(&self) -> Option<&str> {
self.glob.as_ref().map(|s| &**s)
}
/// Return the kind of this error.
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
}
impl ErrorKind {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
Error::InvalidRecursive => { ErrorKind::InvalidRecursive => {
"invalid use of **; must be one path component" "invalid use of **; must be one path component"
} }
Error::UnclosedClass => { ErrorKind::UnclosedClass => {
"unclosed character class; missing ']'" "unclosed character class; missing ']'"
} }
Error::InvalidRange(_, _) => { ErrorKind::InvalidRange(_, _) => {
"invalid character range" "invalid character range"
} }
Error::UnopenedAlternates => { ErrorKind::UnopenedAlternates => {
"unopened alternate group; missing '{' \ "unopened alternate group; missing '{' \
(maybe escape '}' with '[}]'?)" (maybe escape '}' with '[}]'?)"
} }
Error::UnclosedAlternates => { ErrorKind::UnclosedAlternates => {
"unclosed alternate group; missing '}' \ "unclosed alternate group; missing '}' \
(maybe escape '{' with '[{]'?)" (maybe escape '{' with '[{]'?)"
} }
Error::NestedAlternates => { ErrorKind::NestedAlternates => {
"nested alternate groups are not allowed" "nested alternate groups are not allowed"
} }
Error::Regex(ref err) => err, ErrorKind::Regex(ref err) => err,
} }
} }
} }
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.glob {
None => self.kind.fmt(f),
Some(ref glob) => {
write!(f, "error parsing glob '{}': {}", glob, self.kind)
}
}
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
Error::InvalidRecursive ErrorKind::InvalidRecursive
| Error::UnclosedClass | ErrorKind::UnclosedClass
| Error::UnopenedAlternates | ErrorKind::UnopenedAlternates
| Error::UnclosedAlternates | ErrorKind::UnclosedAlternates
| Error::NestedAlternates | ErrorKind::NestedAlternates
| Error::Regex(_) => { | ErrorKind::Regex(_) => {
write!(f, "{}", self.description()) write!(f, "{}", self.description())
} }
Error::InvalidRange(s, e) => { ErrorKind::InvalidRange(s, e) => {
write!(f, "invalid range; '{}' > '{}'", s, e) write!(f, "invalid range; '{}' > '{}'", s, e)
} }
} }
@@ -201,12 +239,22 @@ fn new_regex(pat: &str) -> Result<Regex, Error> {
.size_limit(10 * (1 << 20)) .size_limit(10 * (1 << 20))
.dfa_size_limit(10 * (1 << 20)) .dfa_size_limit(10 * (1 << 20))
.build() .build()
.map_err(|err| Error::Regex(err.to_string())) .map_err(|err| {
Error {
glob: Some(pat.to_string()),
kind: ErrorKind::Regex(err.to_string()),
}
})
} }
fn new_regex_set<I, S>(pats: I) -> Result<RegexSet, Error> fn new_regex_set<I, S>(pats: I) -> Result<RegexSet, Error>
where S: AsRef<str>, I: IntoIterator<Item=S> { where S: AsRef<str>, I: IntoIterator<Item=S> {
RegexSet::new(pats).map_err(|err| Error::Regex(err.to_string())) RegexSet::new(pats).map_err(|err| {
Error {
glob: None,
kind: ErrorKind::Regex(err.to_string()),
}
})
} }
type Fnv = hash::BuildHasherDefault<fnv::FnvHasher>; type Fnv = hash::BuildHasherDefault<fnv::FnvHasher>;

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "ignore" name = "ignore"
version = "0.1.9" #:version version = "0.2.2" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"] authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """ description = """
A fast library for efficiently matching ignore files such as `.gitignore` A fast library for efficiently matching ignore files such as `.gitignore`
@@ -19,7 +19,7 @@ bench = false
[dependencies] [dependencies]
crossbeam = "0.2" crossbeam = "0.2"
globset = { version = "0.1.4", path = "../globset" } globset = { version = "0.2.0", path = "../globset" }
lazy_static = "0.2" lazy_static = "0.2"
log = "0.3" log = "0.3"
memchr = "1" memchr = "1"

View File

@@ -20,7 +20,7 @@ Add this to your `Cargo.toml`:
```toml ```toml
[dependencies] [dependencies]
ignore = "0.1" ignore = "0.2"
``` ```
and this to your crate root: and this to your crate root:

View File

@@ -169,8 +169,8 @@ impl Gitignore {
self.num_whitelists self.num_whitelists
} }
/// Returns whether the given file path matched a pattern in this gitignore /// Returns whether the given path (file or directory) matched a pattern in
/// matcher. /// this gitignore matcher.
/// ///
/// `is_dir` should be true if the path refers to a directory and false /// `is_dir` should be true if the path refers to a directory and false
/// otherwise. /// otherwise.
@@ -191,6 +191,48 @@ impl Gitignore {
self.matched_stripped(self.strip(path.as_ref()), is_dir) self.matched_stripped(self.strip(path.as_ref()), is_dir)
} }
/// Returns whether the given path (file or directory, and expected to be
/// under the root) or any of its parent directories (up to the root)
/// matched a pattern in this gitignore matcher.
///
/// NOTE: This method is more expensive than walking the directory hierarchy
/// top-to-bottom and matching the entries. But, is easier to use in cases
/// when a list of paths are available without a hierarchy.
///
/// `is_dir` should be true if the path refers to a directory and false
/// otherwise.
///
/// The given path is matched relative to the path given when building
/// the matcher. Specifically, before matching `path`, its prefix (as
/// determined by a common suffix of the directory containing this
/// gitignore) is stripped. If there is no common suffix/prefix overlap,
/// then `path` is assumed to be relative to this matcher.
pub fn matched_path_or_any_parents<P: AsRef<Path>>(
&self,
path: P,
is_dir: bool,
) -> Match<&Glob> {
if self.is_empty() {
return Match::None;
}
let mut path = self.strip(path.as_ref());
debug_assert!(
!path.has_root(),
"path is expect to be under the root"
);
match self.matched_stripped(path, is_dir) {
Match::None => (), // walk up
a_match => return a_match,
}
while let Some(parent) = path.parent() {
match self.matched_stripped(parent, /* is_dir */ true) {
Match::None => path = parent, // walk up
a_match => return a_match,
}
}
Match::None
}
/// Like matched, but takes a path that has already been stripped. /// Like matched, but takes a path that has already been stripped.
fn matched_stripped<P: AsRef<Path>>( fn matched_stripped<P: AsRef<Path>>(
&self, &self,
@@ -254,6 +296,7 @@ pub struct GitignoreBuilder {
builder: GlobSetBuilder, builder: GlobSetBuilder,
root: PathBuf, root: PathBuf,
globs: Vec<Glob>, globs: Vec<Glob>,
case_insensitive: bool,
} }
impl GitignoreBuilder { impl GitignoreBuilder {
@@ -269,6 +312,7 @@ impl GitignoreBuilder {
builder: GlobSetBuilder::new(), builder: GlobSetBuilder::new(),
root: strip_prefix("./", root).unwrap_or(root).to_path_buf(), root: strip_prefix("./", root).unwrap_or(root).to_path_buf(),
globs: vec![], globs: vec![],
case_insensitive: false,
} }
} }
@@ -279,7 +323,12 @@ impl GitignoreBuilder {
let nignore = self.globs.iter().filter(|g| !g.is_whitelist()).count(); let nignore = self.globs.iter().filter(|g| !g.is_whitelist()).count();
let nwhite = self.globs.iter().filter(|g| g.is_whitelist()).count(); let nwhite = self.globs.iter().filter(|g| g.is_whitelist()).count();
let set = try!( let set = try!(
self.builder.build().map_err(|err| Error::Glob(err.to_string()))); self.builder.build().map_err(|err| {
Error::Glob {
glob: None,
err: err.to_string(),
}
}));
Ok(Gitignore { Ok(Gitignore {
set: set, set: set,
root: self.root.clone(), root: self.root.clone(),
@@ -419,12 +468,28 @@ impl GitignoreBuilder {
let parsed = try!( let parsed = try!(
GlobBuilder::new(&glob.actual) GlobBuilder::new(&glob.actual)
.literal_separator(literal_separator) .literal_separator(literal_separator)
.case_insensitive(self.case_insensitive)
.build() .build()
.map_err(|err| Error::Glob(err.to_string()))); .map_err(|err| {
Error::Glob {
glob: Some(glob.original.clone()),
err: err.kind().to_string(),
}
}));
self.builder.add(parsed); self.builder.add(parsed);
self.globs.push(glob); self.globs.push(glob);
Ok(self) Ok(self)
} }
/// Toggle whether the globs should be matched case insensitively or not.
///
/// This is disabled by default.
pub fn case_insensitive(
&mut self, yes: bool
) -> Result<&mut GitignoreBuilder, Error> {
self.case_insensitive = yes;
Ok(self)
}
} }
/// Return the file path of the current environment's global gitignore file. /// Return the file path of the current environment's global gitignore file.
@@ -607,4 +672,21 @@ mod tests {
fn regression_106() { fn regression_106() {
gi_from_str("/", " "); gi_from_str("/", " ");
} }
#[test]
fn case_insensitive() {
let gi = GitignoreBuilder::new(ROOT)
.case_insensitive(true).unwrap()
.add_str(None, "*.html").unwrap()
.build().unwrap();
assert!(gi.matched("foo.html", false).is_ignore());
assert!(gi.matched("foo.HTML", false).is_ignore());
assert!(!gi.matched("foo.htm", false).is_ignore());
assert!(!gi.matched("foo.HTM", false).is_ignore());
}
ignored!(cs1, ROOT, "*.html", "foo.html");
not_ignored!(cs2, ROOT, "*.html", "foo.HTML");
not_ignored!(cs3, ROOT, "*.html", "foo.htm");
not_ignored!(cs4, ROOT, "*.html", "foo.HTM");
} }

View File

@@ -112,7 +112,17 @@ pub enum Error {
/// An error that occurs when doing I/O, such as reading an ignore file. /// An error that occurs when doing I/O, such as reading an ignore file.
Io(io::Error), Io(io::Error),
/// An error that occurs when trying to parse a glob. /// An error that occurs when trying to parse a glob.
Glob(String), Glob {
/// The original glob that caused this error. This glob, when
/// available, always corresponds to the glob provided by an end user.
/// e.g., It is the glob as writtein in a `.gitignore` file.
///
/// (This glob may be distinct from the glob that is actually
/// compiled, after accounting for `gitignore` semantics.)
glob: Option<String>,
/// The underlying glob error as a string.
err: String,
},
/// A type selection for a file type that is not defined. /// A type selection for a file type that is not defined.
UnrecognizedFileType(String), UnrecognizedFileType(String),
/// A user specified file type definition could not be parsed. /// A user specified file type definition could not be parsed.
@@ -144,7 +154,7 @@ impl Error {
Error::WithDepth { ref err, .. } => err.is_io(), Error::WithDepth { ref err, .. } => err.is_io(),
Error::Loop { .. } => false, Error::Loop { .. } => false,
Error::Io(_) => true, Error::Io(_) => true,
Error::Glob(_) => false, Error::Glob { .. } => false,
Error::UnrecognizedFileType(_) => false, Error::UnrecognizedFileType(_) => false,
Error::InvalidDefinition => false, Error::InvalidDefinition => false,
} }
@@ -199,7 +209,7 @@ impl error::Error for Error {
Error::WithDepth { ref err, .. } => err.description(), Error::WithDepth { ref err, .. } => err.description(),
Error::Loop { .. } => "file system loop found", Error::Loop { .. } => "file system loop found",
Error::Io(ref err) => err.description(), Error::Io(ref err) => err.description(),
Error::Glob(ref msg) => msg, Error::Glob { ref err, .. } => err,
Error::UnrecognizedFileType(_) => "unrecognized file type", Error::UnrecognizedFileType(_) => "unrecognized file type",
Error::InvalidDefinition => "invalid definition", Error::InvalidDefinition => "invalid definition",
} }
@@ -227,7 +237,10 @@ impl fmt::Display for Error {
child.display(), ancestor.display()) child.display(), ancestor.display())
} }
Error::Io(ref err) => err.fmt(f), Error::Io(ref err) => err.fmt(f),
Error::Glob(ref msg) => write!(f, "{}", msg), Error::Glob { glob: None, ref err } => write!(f, "{}", err),
Error::Glob { glob: Some(ref glob), ref err } => {
write!(f, "error parsing glob '{}': {}", glob, err)
}
Error::UnrecognizedFileType(ref ty) => { Error::UnrecognizedFileType(ref ty) => {
write!(f, "unrecognized file type: {}", ty) write!(f, "unrecognized file type: {}", ty)
} }

View File

@@ -137,6 +137,16 @@ impl OverrideBuilder {
try!(self.builder.add_line(None, glob)); try!(self.builder.add_line(None, glob));
Ok(self) Ok(self)
} }
/// Toggle whether the globs should be matched case insensitively or not.
///
/// This is disabled by default.
pub fn case_insensitive(
&mut self, yes: bool
) -> Result<&mut OverrideBuilder, Error> {
try!(self.builder.case_insensitive(yes));
Ok(self)
}
} }
#[cfg(test)] #[cfg(test)]
@@ -220,4 +230,27 @@ mod tests {
let ov = ov(&["!/bar"]); let ov = ov(&["!/bar"]);
assert!(ov.matched("./foo/bar", false).is_none()); assert!(ov.matched("./foo/bar", false).is_none());
} }
#[test]
fn case_insensitive() {
let ov = OverrideBuilder::new(ROOT)
.case_insensitive(true).unwrap()
.add("*.html").unwrap()
.build().unwrap();
assert!(ov.matched("foo.html", false).is_whitelist());
assert!(ov.matched("foo.HTML", false).is_whitelist());
assert!(ov.matched("foo.htm", false).is_ignore());
assert!(ov.matched("foo.HTM", false).is_ignore());
}
#[test]
fn default_case_sensitive() {
let ov = OverrideBuilder::new(ROOT)
.add("*.html").unwrap()
.build().unwrap();
assert!(ov.matched("foo.html", false).is_whitelist());
assert!(ov.matched("foo.HTML", false).is_ignore());
assert!(ov.matched("foo.htm", false).is_ignore());
assert!(ov.matched("foo.HTM", false).is_ignore());
}
} }

View File

@@ -101,7 +101,9 @@ const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]), ("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
("asm", &["*.asm", "*.s", "*.S"]), ("asm", &["*.asm", "*.s", "*.S"]),
("awk", &["*.awk"]), ("awk", &["*.awk"]),
("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
("c", &["*.c", "*.h", "*.H"]), ("c", &["*.c", "*.h", "*.H"]),
("cabal", &["*.cabal"]),
("cbor", &["*.cbor"]), ("cbor", &["*.cbor"]),
("ceylon", &["*.ceylon"]), ("ceylon", &["*.ceylon"]),
("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]), ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
@@ -111,11 +113,12 @@ const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
("config", &["*.config"]), ("config", &["*.config"]),
("cpp", &[ ("cpp", &[
"*.C", "*.cc", "*.cpp", "*.cxx", "*.C", "*.cc", "*.cpp", "*.cxx",
"*.h", "*.H", "*.hh", "*.hpp", "*.h", "*.H", "*.hh", "*.hpp", "*.inl",
]), ]),
("crystal", &["Projectfile", "*.cr"]), ("crystal", &["Projectfile", "*.cr"]),
("cs", &["*.cs"]), ("cs", &["*.cs"]),
("csharp", &["*.cs"]), ("csharp", &["*.cs"]),
("cshtml", &["*.cshtml"]),
("css", &["*.css", "*.scss"]), ("css", &["*.css", "*.scss"]),
("cython", &["*.pyx"]), ("cython", &["*.pyx"]),
("dart", &["*.dart"]), ("dart", &["*.dart"]),
@@ -142,19 +145,23 @@ const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
]), ]),
("json", &["*.json"]), ("json", &["*.json"]),
("jsonl", &["*.jsonl"]), ("jsonl", &["*.jsonl"]),
("julia", &["*.jl"]),
("jl", &["*.jl"]),
("kotlin", &["*.kt", "*.kts"]), ("kotlin", &["*.kt", "*.kts"]),
("less", &["*.less"]), ("less", &["*.less"]),
("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]), ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
("log", &["*.log"]), ("log", &["*.log"]),
("lua", &["*.lua"]), ("lua", &["*.lua"]),
("m4", &["*.ac", "*.m4"]), ("m4", &["*.ac", "*.m4"]),
("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk", "*.mak"]), ("make", &["gnumakefile", "Gnumakefile", "GNUmakefile", "makefile", "Makefile", "*.mk", "*.mak"]),
("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]), ("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]), ("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
("matlab", &["*.m"]), ("matlab", &["*.m"]),
("mk", &["mkfile"]), ("mk", &["mkfile"]),
("ml", &["*.ml"]), ("ml", &["*.ml"]),
("msbuild", &["*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets"]),
("nim", &["*.nim"]), ("nim", &["*.nim"]),
("nix", &["*.nix"]),
("objc", &["*.h", "*.m"]), ("objc", &["*.h", "*.m"]),
("objcpp", &["*.h", "*.mm"]), ("objcpp", &["*.h", "*.mm"]),
("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]), ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
@@ -165,6 +172,7 @@ const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
("pod", &["*.pod"]), ("pod", &["*.pod"]),
("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]), ("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
("py", &["*.py"]), ("py", &["*.py"]),
("qmake", &["*.pro", "*.pri", "*.prf"]),
("readme", &["README*", "*README"]), ("readme", &["README*", "*README"]),
("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]), ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
("rdoc", &["*.rdoc"]), ("rdoc", &["*.rdoc"]),
@@ -447,13 +455,18 @@ impl TypesBuilder {
GlobBuilder::new(glob) GlobBuilder::new(glob)
.literal_separator(true) .literal_separator(true)
.build() .build()
.map_err(|err| Error::Glob(err.to_string())))); .map_err(|err| {
Error::Glob {
glob: Some(glob.to_string()),
err: err.kind().to_string(),
}
})));
glob_to_selection.push((isel, iglob)); glob_to_selection.push((isel, iglob));
} }
selections.push(selection.clone().map(move |_| def)); selections.push(selection.clone().map(move |_| def));
} }
let set = try!(build_set.build().map_err(|err| { let set = try!(build_set.build().map_err(|err| {
Error::Glob(err.to_string()) Error::Glob { glob: None, err: err.to_string() }
})); }));
Ok(Types { Ok(Types {
defs: defs, defs: defs,

View File

@@ -380,16 +380,16 @@ impl DirEntryRaw {
/// is: `.ignore`, `.gitignore`, `.git/info/exclude`, global gitignore and /// is: `.ignore`, `.gitignore`, `.git/info/exclude`, global gitignore and
/// finally explicitly added ignore files. Note that precedence between /// finally explicitly added ignore files. Note that precedence between
/// different types of ignore files is not impacted by the directory hierarchy; /// different types of ignore files is not impacted by the directory hierarchy;
/// any `.ignore` file overrides all `.gitignore` files. Within each /// any `.ignore` file overrides all `.gitignore` files. Within each precedence
/// precedence level, more nested ignore files have a higher precedence over /// level, more nested ignore files have a higher precedence than less nested
/// less nested ignore files. /// ignore files.
/// * Third, if the previous step yields an ignore match, than all matching /// * Third, if the previous step yields an ignore match, then all matching
/// is stopped and the path is skipped.. If it yields a whitelist match, then /// is stopped and the path is skipped. If it yields a whitelist match, then
/// process continues. A whitelist match can be overridden by a later matcher. /// matching continues. A whitelist match can be overridden by a later matcher.
/// * Fourth, unless the path is a directory, the file type matcher is run on /// * Fourth, unless the path is a directory, the file type matcher is run on
/// the path. As above, if it's an ignore match, then all matching is stopped /// the path. As above, if it yields an ignore match, then all matching is
/// and the path is skipped. If it's a whitelist match, then matching /// stopped and the path is skipped. If it yields a whitelist match, then
/// continues. /// matching continues.
/// * Fifth, if the path hasn't been whitelisted and it is hidden, then the /// * Fifth, if the path hasn't been whitelisted and it is hidden, then the
/// path is skipped. /// path is skipped.
/// * Sixth, unless the path is a directory, the size of the file is compared /// * Sixth, unless the path is a directory, the size of the file is compared

View File

@@ -0,0 +1,216 @@
# Based on https://github.com/behnam/gitignore-test/blob/master/.gitignore
### file in root
# MATCH /file_root_1
file_root_00
# NO_MATCH
file_root_01/
# NO_MATCH
file_root_02/*
# NO_MATCH
file_root_03/**
# MATCH /file_root_10
/file_root_10
# NO_MATCH
/file_root_11/
# NO_MATCH
/file_root_12/*
# NO_MATCH
/file_root_13/**
# NO_MATCH
*/file_root_20
# NO_MATCH
*/file_root_21/
# NO_MATCH
*/file_root_22/*
# NO_MATCH
*/file_root_23/**
# MATCH /file_root_30
**/file_root_30
# NO_MATCH
**/file_root_31/
# NO_MATCH
**/file_root_32/*
# NO_MATCH
**/file_root_33/**
### file in sub-dir
# MATCH /parent_dir/file_deep_1
file_deep_00
# NO_MATCH
file_deep_01/
# NO_MATCH
file_deep_02/*
# NO_MATCH
file_deep_03/**
# NO_MATCH
/file_deep_10
# NO_MATCH
/file_deep_11/
# NO_MATCH
/file_deep_12/*
# NO_MATCH
/file_deep_13/**
# MATCH /parent_dir/file_deep_20
*/file_deep_20
# NO_MATCH
*/file_deep_21/
# NO_MATCH
*/file_deep_22/*
# NO_MATCH
*/file_deep_23/**
# MATCH /parent_dir/file_deep_30
**/file_deep_30
# NO_MATCH
**/file_deep_31/
# NO_MATCH
**/file_deep_32/*
# NO_MATCH
**/file_deep_33/**
### dir in root
# MATCH /dir_root_00
dir_root_00
# MATCH /dir_root_01
dir_root_01/
# MATCH /dir_root_02
dir_root_02/*
# MATCH /dir_root_03
dir_root_03/**
# MATCH /dir_root_10
/dir_root_10
# MATCH /dir_root_11
/dir_root_11/
# MATCH /dir_root_12
/dir_root_12/*
# MATCH /dir_root_13
/dir_root_13/**
# NO_MATCH
*/dir_root_20
# NO_MATCH
*/dir_root_21/
# NO_MATCH
*/dir_root_22/*
# NO_MATCH
*/dir_root_23/**
# MATCH /dir_root_30
**/dir_root_30
# MATCH /dir_root_31
**/dir_root_31/
# MATCH /dir_root_32
**/dir_root_32/*
# MATCH /dir_root_33
**/dir_root_33/**
### dir in sub-dir
# MATCH /parent_dir/dir_deep_00
dir_deep_00
# MATCH /parent_dir/dir_deep_01
dir_deep_01/
# NO_MATCH
dir_deep_02/*
# NO_MATCH
dir_deep_03/**
# NO_MATCH
/dir_deep_10
# NO_MATCH
/dir_deep_11/
# NO_MATCH
/dir_deep_12/*
# NO_MATCH
/dir_deep_13/**
# MATCH /parent_dir/dir_deep_20
*/dir_deep_20
# MATCH /parent_dir/dir_deep_21
*/dir_deep_21/
# MATCH /parent_dir/dir_deep_22
*/dir_deep_22/*
# MATCH /parent_dir/dir_deep_23
*/dir_deep_23/**
# MATCH /parent_dir/dir_deep_30
**/dir_deep_30
# MATCH /parent_dir/dir_deep_31
**/dir_deep_31/
# MATCH /parent_dir/dir_deep_32
**/dir_deep_32/*
# MATCH /parent_dir/dir_deep_33
**/dir_deep_33/**

View File

@@ -0,0 +1,297 @@
extern crate ignore;
use std::path::Path;
use ignore::gitignore::{Gitignore, GitignoreBuilder};
const IGNORE_FILE: &'static str = "tests/gitignore_matched_path_or_any_parents_tests.gitignore";
fn get_gitignore() -> Gitignore {
let mut builder = GitignoreBuilder::new("ROOT");
let error = builder.add(IGNORE_FILE);
assert!(error.is_none(), "failed to open gitignore file");
builder.build().unwrap()
}
#[test]
#[should_panic(expected = "path is expect to be under the root")]
fn test_path_should_be_under_root() {
let gitignore = get_gitignore();
let path = "/tmp/some_file";
gitignore.matched_path_or_any_parents(Path::new(path), false);
assert!(false);
}
#[test]
fn test_files_in_root() {
let gitignore = get_gitignore();
let m = |path: &str| gitignore.matched_path_or_any_parents(Path::new(path), false);
// 0x
assert!(m("ROOT/file_root_00").is_ignore());
assert!(m("ROOT/file_root_01").is_none());
assert!(m("ROOT/file_root_02").is_none());
assert!(m("ROOT/file_root_03").is_none());
// 1x
assert!(m("ROOT/file_root_10").is_ignore());
assert!(m("ROOT/file_root_11").is_none());
assert!(m("ROOT/file_root_12").is_none());
assert!(m("ROOT/file_root_13").is_none());
// 2x
assert!(m("ROOT/file_root_20").is_none());
assert!(m("ROOT/file_root_21").is_none());
assert!(m("ROOT/file_root_22").is_none());
assert!(m("ROOT/file_root_23").is_none());
// 3x
assert!(m("ROOT/file_root_30").is_ignore());
assert!(m("ROOT/file_root_31").is_none());
assert!(m("ROOT/file_root_32").is_none());
assert!(m("ROOT/file_root_33").is_none());
}
#[test]
fn test_files_in_deep() {
let gitignore = get_gitignore();
let m = |path: &str| gitignore.matched_path_or_any_parents(Path::new(path), false);
// 0x
assert!(m("ROOT/parent_dir/file_deep_00").is_ignore());
assert!(m("ROOT/parent_dir/file_deep_01").is_none());
assert!(m("ROOT/parent_dir/file_deep_02").is_none());
assert!(m("ROOT/parent_dir/file_deep_03").is_none());
// 1x
assert!(m("ROOT/parent_dir/file_deep_10").is_none());
assert!(m("ROOT/parent_dir/file_deep_11").is_none());
assert!(m("ROOT/parent_dir/file_deep_12").is_none());
assert!(m("ROOT/parent_dir/file_deep_13").is_none());
// 2x
assert!(m("ROOT/parent_dir/file_deep_20").is_ignore());
assert!(m("ROOT/parent_dir/file_deep_21").is_none());
assert!(m("ROOT/parent_dir/file_deep_22").is_none());
assert!(m("ROOT/parent_dir/file_deep_23").is_none());
// 3x
assert!(m("ROOT/parent_dir/file_deep_30").is_ignore());
assert!(m("ROOT/parent_dir/file_deep_31").is_none());
assert!(m("ROOT/parent_dir/file_deep_32").is_none());
assert!(m("ROOT/parent_dir/file_deep_33").is_none());
}
#[test]
fn test_dirs_in_root() {
let gitignore = get_gitignore();
let m =
|path: &str, is_dir: bool| gitignore.matched_path_or_any_parents(Path::new(path), is_dir);
// 00
assert!(m("ROOT/dir_root_00", true).is_ignore());
assert!(m("ROOT/dir_root_00/file", false).is_ignore());
assert!(m("ROOT/dir_root_00/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_00/child_dir/file", false).is_ignore());
// 01
assert!(m("ROOT/dir_root_01", true).is_ignore());
assert!(m("ROOT/dir_root_01/file", false).is_ignore());
assert!(m("ROOT/dir_root_01/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_01/child_dir/file", false).is_ignore());
// 02
assert!(m("ROOT/dir_root_02", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/dir_root_02/file", false).is_ignore());
assert!(m("ROOT/dir_root_02/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_02/child_dir/file", false).is_ignore());
// 03
assert!(m("ROOT/dir_root_03", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/dir_root_03/file", false).is_ignore());
assert!(m("ROOT/dir_root_03/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_03/child_dir/file", false).is_ignore());
// 10
assert!(m("ROOT/dir_root_10", true).is_ignore());
assert!(m("ROOT/dir_root_10/file", false).is_ignore());
assert!(m("ROOT/dir_root_10/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_10/child_dir/file", false).is_ignore());
// 11
assert!(m("ROOT/dir_root_11", true).is_ignore());
assert!(m("ROOT/dir_root_11/file", false).is_ignore());
assert!(m("ROOT/dir_root_11/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_11/child_dir/file", false).is_ignore());
// 12
assert!(m("ROOT/dir_root_12", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/dir_root_12/file", false).is_ignore());
assert!(m("ROOT/dir_root_12/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_12/child_dir/file", false).is_ignore());
// 13
assert!(m("ROOT/dir_root_13", true).is_none());
assert!(m("ROOT/dir_root_13/file", false).is_ignore());
assert!(m("ROOT/dir_root_13/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_13/child_dir/file", false).is_ignore());
// 20
assert!(m("ROOT/dir_root_20", true).is_none());
assert!(m("ROOT/dir_root_20/file", false).is_none());
assert!(m("ROOT/dir_root_20/child_dir", true).is_none());
assert!(m("ROOT/dir_root_20/child_dir/file", false).is_none());
// 21
assert!(m("ROOT/dir_root_21", true).is_none());
assert!(m("ROOT/dir_root_21/file", false).is_none());
assert!(m("ROOT/dir_root_21/child_dir", true).is_none());
assert!(m("ROOT/dir_root_21/child_dir/file", false).is_none());
// 22
assert!(m("ROOT/dir_root_22", true).is_none());
assert!(m("ROOT/dir_root_22/file", false).is_none());
assert!(m("ROOT/dir_root_22/child_dir", true).is_none());
assert!(m("ROOT/dir_root_22/child_dir/file", false).is_none());
// 23
assert!(m("ROOT/dir_root_23", true).is_none());
assert!(m("ROOT/dir_root_23/file", false).is_none());
assert!(m("ROOT/dir_root_23/child_dir", true).is_none());
assert!(m("ROOT/dir_root_23/child_dir/file", false).is_none());
// 30
assert!(m("ROOT/dir_root_30", true).is_ignore());
assert!(m("ROOT/dir_root_30/file", false).is_ignore());
assert!(m("ROOT/dir_root_30/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_30/child_dir/file", false).is_ignore());
// 31
assert!(m("ROOT/dir_root_31", true).is_ignore());
assert!(m("ROOT/dir_root_31/file", false).is_ignore());
assert!(m("ROOT/dir_root_31/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_31/child_dir/file", false).is_ignore());
// 32
assert!(m("ROOT/dir_root_32", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/dir_root_32/file", false).is_ignore());
assert!(m("ROOT/dir_root_32/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_32/child_dir/file", false).is_ignore());
// 33
assert!(m("ROOT/dir_root_33", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/dir_root_33/file", false).is_ignore());
assert!(m("ROOT/dir_root_33/child_dir", true).is_ignore());
assert!(m("ROOT/dir_root_33/child_dir/file", false).is_ignore());
}
#[test]
fn test_dirs_in_deep() {
let gitignore = get_gitignore();
let m =
|path: &str, is_dir: bool| gitignore.matched_path_or_any_parents(Path::new(path), is_dir);
// 00
assert!(m("ROOT/parent_dir/dir_deep_00", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_00/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_00/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_00/child_dir/file", false).is_ignore());
// 01
assert!(m("ROOT/parent_dir/dir_deep_01", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_01/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_01/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_01/child_dir/file", false).is_ignore());
// 02
assert!(m("ROOT/parent_dir/dir_deep_02", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/parent_dir/dir_deep_02/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_02/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_02/child_dir/file", false).is_ignore());
// 03
assert!(m("ROOT/parent_dir/dir_deep_03", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/parent_dir/dir_deep_03/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_03/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_03/child_dir/file", false).is_ignore());
// 10
assert!(m("ROOT/parent_dir/dir_deep_10", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_10/file", false).is_none());
assert!(m("ROOT/parent_dir/dir_deep_10/child_dir", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_10/child_dir/file", false).is_none());
// 11
assert!(m("ROOT/parent_dir/dir_deep_11", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_11/file", false).is_none());
assert!(m("ROOT/parent_dir/dir_deep_11/child_dir", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_11/child_dir/file", false).is_none());
// 12
assert!(m("ROOT/parent_dir/dir_deep_12", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_12/file", false).is_none());
assert!(m("ROOT/parent_dir/dir_deep_12/child_dir", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_12/child_dir/file", false).is_none());
// 13
assert!(m("ROOT/parent_dir/dir_deep_13", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_13/file", false).is_none());
assert!(m("ROOT/parent_dir/dir_deep_13/child_dir", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_13/child_dir/file", false).is_none());
// 20
assert!(m("ROOT/parent_dir/dir_deep_20", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_20/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_20/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_20/child_dir/file", false).is_ignore());
// 21
assert!(m("ROOT/parent_dir/dir_deep_21", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_21/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_21/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_21/child_dir/file", false).is_ignore());
// 22
assert!(m("ROOT/parent_dir/dir_deep_22", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/parent_dir/dir_deep_22/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_22/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_22/child_dir/file", false).is_ignore());
// 23
assert!(m("ROOT/parent_dir/dir_deep_23", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/parent_dir/dir_deep_23/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_23/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_23/child_dir/file", false).is_ignore());
// 30
assert!(m("ROOT/parent_dir/dir_deep_30", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_30/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_30/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_30/child_dir/file", false).is_ignore());
// 31
assert!(m("ROOT/parent_dir/dir_deep_31", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_31/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_31/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_31/child_dir/file", false).is_ignore());
// 32
assert!(m("ROOT/parent_dir/dir_deep_32", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/parent_dir/dir_deep_32/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_32/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_32/child_dir/file", false).is_ignore());
// 33
assert!(m("ROOT/parent_dir/dir_deep_33", true).is_none()); // dir itself doesn't match
assert!(m("ROOT/parent_dir/dir_deep_33/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_33/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_33/child_dir/file", false).is_ignore());
}

View File

@@ -1,9 +1,9 @@
class RipgrepBin < Formula class RipgrepBin < Formula
version '0.5.0' version '0.5.2'
desc "Search tool like grep and The Silver Searcher." desc "Search tool like grep and The Silver Searcher."
homepage "https://github.com/BurntSushi/ripgrep" homepage "https://github.com/BurntSushi/ripgrep"
url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-x86_64-apple-darwin.tar.gz" url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-x86_64-apple-darwin.tar.gz"
sha256 "5bfa8872c4f2a5d010ddec1c213d518056e62d4dd3b3f23a0ef099b85343dbdd" sha256 "a0326a84af8517ad707d8c7cccba6e112de27822c391cc0937e4727fbb6c48f4"
conflicts_with "ripgrep" conflicts_with "ripgrep"

15
snapcraft.yaml Normal file
View File

@@ -0,0 +1,15 @@
name: ripgrep # you probably want to 'snapcraft register <name>'
version: '0.5.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: Fast file searcher # 79 char long summary
description: |
ripgrep combines the usability of The Silver Searcher with the raw speed of grep.
grade: stable # must be 'stable' to release into candidate/stable channels
confinement: classic # use 'strict' once you have the right plugs and slots
parts:
ripgrep:
plugin: rust
source: .
apps:
rg:
command: env PATH=$SNAP/bin:$PATH rg
aliases: [rg]

View File

@@ -9,15 +9,19 @@ ripgrep's regex engine uses finite automata and guarantees linear time
searching. Because of this, features like backreferences and arbitrary searching. Because of this, features like backreferences and arbitrary
lookaround are not supported. lookaround are not supported.
Note that ripgrep may abort unexpectedly when using default settings if it
searches a file that is simultaneously truncated. This behavior can be avoided
by passing the --no-mmap flag.
Project home page: https://github.com/BurntSushi/ripgrep Project home page: https://github.com/BurntSushi/ripgrep
Use -h for short descriptions and --help for more details."; Use -h for short descriptions and --help for more details.";
const USAGE: &'static str = " const USAGE: &'static str = "
rg [OPTIONS] <pattern> [<path> ...] rg [options] PATTERN [path ...]
rg [OPTIONS] [-e PATTERN | -f FILE ]... [<path> ...] rg [options] [-e PATTERN ...] [-f FILE ...] [path ...]
rg [OPTIONS] --files [<path> ...] rg [options] --files [path ...]
rg [OPTIONS] --type-list"; rg [options] --type-list";
const TEMPLATE: &'static str = "\ const TEMPLATE: &'static str = "\
{bin} {version} {bin} {version}
@@ -41,21 +45,24 @@ OPTIONS:
/// in a `build.rs` script to build shell completion files. /// in a `build.rs` script to build shell completion files.
pub fn app() -> App<'static, 'static> { pub fn app() -> App<'static, 'static> {
let arg = |name| { let arg = |name| {
Arg::with_name(name).help(USAGES[name].short).long_help(USAGES[name].long) Arg::with_name(name)
.help(USAGES[name].short)
.long_help(USAGES[name].long)
}; };
let flag = |name| arg(name).long(name); let flag = |name| arg(name).long(name);
App::new("ripgrep") App::new("ripgrep")
.author(crate_authors!()) .author(crate_authors!())
.version(crate_version!()) .version(crate_version!())
.long_version(LONG_VERSION.as_str())
.about(ABOUT) .about(ABOUT)
.max_term_width(100) .max_term_width(100)
.setting(AppSettings::UnifiedHelpMessage) .setting(AppSettings::UnifiedHelpMessage)
.usage(USAGE) .usage(USAGE)
.template(TEMPLATE) .template(TEMPLATE)
.help_message("Prints help information. Use --help for more details.") .help_message("Prints help information. Use --help for more details.")
// First, set up primary positional/flag arguments. // First, set up primary positional/flag arguments.
.arg(arg("pattern") .arg(arg("PATTERN")
.required_unless_one(&[ .required_unless_one(&[
"file", "files", "help-short", "help", "regexp", "type-list", "file", "files", "help-short", "help", "regexp", "type-list",
"ripgrep-version", "ripgrep-version",
@@ -64,13 +71,13 @@ pub fn app() -> App<'static, 'static> {
.arg(flag("regexp").short("e") .arg(flag("regexp").short("e")
.takes_value(true).multiple(true).number_of_values(1) .takes_value(true).multiple(true).number_of_values(1)
.set(ArgSettings::AllowLeadingHyphen) .set(ArgSettings::AllowLeadingHyphen)
.value_name("pattern")) .value_name("PATTERN"))
.arg(flag("files") .arg(flag("files")
// This should also conflict with `pattern`, but the first file // This should also conflict with `PATTERN`, but the first file
// path will actually be in `pattern`. // path will actually be in `PATTERN`.
.conflicts_with_all(&["file", "regexp", "type-list"])) .conflicts_with_all(&["file", "regexp", "type-list"]))
.arg(flag("type-list") .arg(flag("type-list")
.conflicts_with_all(&["file", "files", "pattern", "regexp"])) .conflicts_with_all(&["file", "files", "PATTERN", "regexp"]))
// Second, set up common flags. // Second, set up common flags.
.arg(flag("text").short("a")) .arg(flag("text").short("a"))
.arg(flag("count").short("c")) .arg(flag("count").short("c"))
@@ -78,7 +85,8 @@ pub fn app() -> App<'static, 'static> {
.value_name("WHEN") .value_name("WHEN")
.takes_value(true) .takes_value(true)
.hide_possible_values(true) .hide_possible_values(true)
.possible_values(&["never", "auto", "always", "ansi"])) .possible_values(&["never", "auto", "always", "ansi"])
.default_value_if("vimgrep", None, "never"))
.arg(flag("colors").value_name("SPEC") .arg(flag("colors").value_name("SPEC")
.takes_value(true).multiple(true).number_of_values(1)) .takes_value(true).multiple(true).number_of_values(1))
.arg(flag("encoding").short("E").value_name("ENCODING") .arg(flag("encoding").short("E").value_name("ENCODING")
@@ -86,10 +94,15 @@ pub fn app() -> App<'static, 'static> {
.arg(flag("fixed-strings").short("F")) .arg(flag("fixed-strings").short("F"))
.arg(flag("glob").short("g") .arg(flag("glob").short("g")
.takes_value(true).multiple(true).number_of_values(1) .takes_value(true).multiple(true).number_of_values(1)
.set(ArgSettings::AllowLeadingHyphen)
.value_name("GLOB"))
.arg(flag("iglob")
.takes_value(true).multiple(true).number_of_values(1)
.set(ArgSettings::AllowLeadingHyphen)
.value_name("GLOB")) .value_name("GLOB"))
.arg(flag("ignore-case").short("i")) .arg(flag("ignore-case").short("i"))
.arg(flag("line-number").short("n")) .arg(flag("line-number").short("n"))
.arg(flag("no-line-number").short("N")) .arg(flag("no-line-number").short("N").overrides_with("line-number"))
.arg(flag("quiet").short("q")) .arg(flag("quiet").short("q"))
.arg(flag("type").short("t") .arg(flag("type").short("t")
.takes_value(true).multiple(true).number_of_values(1) .takes_value(true).multiple(true).number_of_values(1)
@@ -100,7 +113,8 @@ pub fn app() -> App<'static, 'static> {
.arg(flag("unrestricted").short("u") .arg(flag("unrestricted").short("u")
.multiple(true)) .multiple(true))
.arg(flag("invert-match").short("v")) .arg(flag("invert-match").short("v"))
.arg(flag("word-regexp").short("w")) .arg(flag("word-regexp").short("w").overrides_with("line-regexp"))
.arg(flag("line-regexp").short("x"))
// Third, set up less common flags. // Third, set up less common flags.
.arg(flag("after-context").short("A") .arg(flag("after-context").short("A")
.value_name("NUM").takes_value(true) .value_name("NUM").takes_value(true)
@@ -114,19 +128,23 @@ pub fn app() -> App<'static, 'static> {
.arg(flag("column")) .arg(flag("column"))
.arg(flag("context-separator") .arg(flag("context-separator")
.value_name("SEPARATOR").takes_value(true)) .value_name("SEPARATOR").takes_value(true))
.arg(flag("dfa-size-limit")
.value_name("NUM+SUFFIX?").takes_value(true))
.arg(flag("debug")) .arg(flag("debug"))
.arg(flag("file").short("f") .arg(flag("file").short("f")
.value_name("FILE").takes_value(true) .value_name("FILE").takes_value(true)
.set(ArgSettings::AllowLeadingHyphen)
.multiple(true).number_of_values(1)) .multiple(true).number_of_values(1))
.arg(flag("files-with-matches").short("l")) .arg(flag("files-with-matches").short("l"))
.arg(flag("files-without-match")) .arg(flag("files-without-match"))
.arg(flag("with-filename").short("H")) .arg(flag("with-filename").short("H"))
.arg(flag("no-filename")) .arg(flag("no-filename").overrides_with("with-filename"))
.arg(flag("heading").overrides_with("no-heading")) .arg(flag("heading"))
.arg(flag("no-heading").overrides_with("heading")) .arg(flag("no-heading").overrides_with("heading"))
.arg(flag("hidden")) .arg(flag("hidden"))
.arg(flag("ignore-file") .arg(flag("ignore-file")
.value_name("FILE").takes_value(true) .value_name("FILE").takes_value(true)
.set(ArgSettings::AllowLeadingHyphen)
.multiple(true).number_of_values(1)) .multiple(true).number_of_values(1))
.arg(flag("follow").short("L")) .arg(flag("follow").short("L"))
.arg(flag("max-count") .arg(flag("max-count")
@@ -147,14 +165,18 @@ pub fn app() -> App<'static, 'static> {
.arg(flag("only-matching").short("o").conflicts_with("replace")) .arg(flag("only-matching").short("o").conflicts_with("replace"))
.arg(flag("path-separator").value_name("SEPARATOR").takes_value(true)) .arg(flag("path-separator").value_name("SEPARATOR").takes_value(true))
.arg(flag("pretty").short("p")) .arg(flag("pretty").short("p"))
.arg(flag("replace").short("r").value_name("ARG").takes_value(true)) .arg(flag("replace").short("r")
.set(ArgSettings::AllowLeadingHyphen)
.value_name("ARG").takes_value(true))
.arg(flag("regex-size-limit")
.value_name("NUM+SUFFIX?").takes_value(true))
.arg(flag("case-sensitive").short("s")) .arg(flag("case-sensitive").short("s"))
.arg(flag("smart-case").short("S")) .arg(flag("smart-case").short("S"))
.arg(flag("sort-files")) .arg(flag("sort-files"))
.arg(flag("threads") .arg(flag("threads")
.short("j").value_name("ARG").takes_value(true) .short("j").value_name("ARG").takes_value(true)
.validator(validate_number)) .validator(validate_number))
.arg(flag("vimgrep")) .arg(flag("vimgrep").overrides_with("count"))
.arg(flag("max-columns").short("M") .arg(flag("max-columns").short("M")
.value_name("NUM").takes_value(true) .value_name("NUM").takes_value(true)
.validator(validate_number)) .validator(validate_number))
@@ -184,6 +206,24 @@ macro_rules! doc {
} }
lazy_static! { lazy_static! {
static ref LONG_VERSION: String = {
let mut features: Vec<&str> = vec![];
if cfg!(feature = "avx-accel") {
features.push("+AVX");
} else {
features.push("-AVX");
}
if cfg!(feature = "simd-accel") {
features.push("+SIMD");
} else {
features.push("-SIMD");
}
format!("{}\n{}", crate_version!(), features.join(" "))
};
static ref USAGES: HashMap<&'static str, Usage> = { static ref USAGES: HashMap<&'static str, Usage> = {
let mut h = HashMap::new(); let mut h = HashMap::new();
doc!(h, "help-short", doc!(h, "help-short",
@@ -195,14 +235,15 @@ lazy_static! {
doc!(h, "ripgrep-version", doc!(h, "ripgrep-version",
"Prints version information."); "Prints version information.");
doc!(h, "pattern", doc!(h, "PATTERN",
"A regular expression used for searching.", "A regular expression used for searching.",
"A regular expression used for searching. Multiple patterns \ "A regular expression used for searching. To match a pattern \
may be given. To match a pattern beginning with a -, use [-]."); beginning with a dash, use the -e/--regexp option.");
doc!(h, "regexp", doc!(h, "regexp",
"A regular expression used for searching.", "Use pattern to search.",
"A regular expression used for searching. Multiple patterns \ "Use pattern to search. This option can be provided multiple \
may be given. To match a pattern beginning with a -, use [-]."); times, where all patterns given are searched. This is also \
useful when searching for patterns that start with a dash.");
doc!(h, "path", doc!(h, "path",
"A file or directory to search.", "A file or directory to search.",
"A file or directory to search. Directories are searched \ "A file or directory to search. Directories are searched \
@@ -222,11 +263,11 @@ lazy_static! {
"Only show count of matches for each file."); "Only show count of matches for each file.");
doc!(h, "color", doc!(h, "color",
"When to use color. [default: auto]", "When to use color. [default: auto]",
"When to use color in the output. The possible values are \ "When to use color in the output. The possible values are never, \
never, auto, always or ansi. The default is auto. When always \ auto, always or ansi. The default is auto. When always is used, \
is used, coloring is attempted based on your environment. When \ coloring is attempted based on your environment. When ansi is \
ansi used, coloring is forcefully done using ANSI escape color \ used, coloring is forcefully done using ANSI escape color \
codes."); codes.");
doc!(h, "colors", doc!(h, "colors",
"Configure color settings and styles.", "Configure color settings and styles.",
"This flag specifies color settings for use in the output. \ "This flag specifies color settings for use in the output. \
@@ -262,6 +303,13 @@ lazy_static! {
ignore logic. Multiple glob flags may be used. Globbing \ ignore logic. Multiple glob flags may be used. Globbing \
rules match .gitignore globs. Precede a glob with a ! \ rules match .gitignore globs. Precede a glob with a ! \
to exclude it."); to exclude it.");
doc!(h, "iglob",
"Include or exclude files/directories case insensitively.",
"Include or exclude files/directories for searching that \
match the given glob. This always overrides any other \
ignore logic. Multiple glob flags may be used. Globbing \
rules match .gitignore globs. Precede a glob with a ! \
to exclude it. Globs are matched case insensitively.");
doc!(h, "ignore-case", doc!(h, "ignore-case",
"Case insensitive search.", "Case insensitive search.",
"Case insensitive search. This is overridden by \ "Case insensitive search. This is overridden by \
@@ -305,6 +353,10 @@ lazy_static! {
"Only show matches surrounded by word boundaries. This is \ "Only show matches surrounded by word boundaries. This is \
equivalent to putting \\b before and after all of the search \ equivalent to putting \\b before and after all of the search \
patterns."); patterns.");
doc!(h, "line-regexp",
"Only show matches surrounded by line boundaries.",
"Only show matches surrounded by line boundaries. This is \
equivalent to putting ^...$ around all of the search patterns.");
doc!(h, "after-context", doc!(h, "after-context",
"Show NUM lines after each match."); "Show NUM lines after each match.");
@@ -326,6 +378,13 @@ lazy_static! {
doc!(h, "debug", doc!(h, "debug",
"Show debug messages.", "Show debug messages.",
"Show debug messages. Please use this when filing a bug report."); "Show debug messages. Please use this when filing a bug report.");
doc!(h, "dfa-size-limit",
"The upper size limit of the generated dfa.",
"The upper size limit of the generated dfa. The default limit is \
10M. This should only be changed on very large regex inputs \
where the (slower) fallback regex engine may otherwise be used. \
\n\nThe argument accepts the same size suffixes as allowed in \
the 'max-filesize' argument.");
doc!(h, "file", doc!(h, "file",
"Search for patterns from the given file.", "Search for patterns from the given file.",
"Search for patterns from the given file, with one pattern per \ "Search for patterns from the given file, with one pattern per \
@@ -434,7 +493,7 @@ lazy_static! {
default when the environment demands it (e.g., cygwin). A path \ default when the environment demands it (e.g., cygwin). A path \
separator is limited to a single byte."); separator is limited to a single byte.");
doc!(h, "pretty", doc!(h, "pretty",
"Alias for --color always --heading -n."); "Alias for --color always --heading --line-number.");
doc!(h, "replace", doc!(h, "replace",
"Replace matches with string given.", "Replace matches with string given.",
"Replace every match with the string given when printing \ "Replace every match with the string given when printing \
@@ -444,6 +503,11 @@ lazy_static! {
Note that the replacement by default replaces each match, and \ Note that the replacement by default replaces each match, and \
NOT the entire line. To replace the entire line, you should \ NOT the entire line. To replace the entire line, you should \
match the entire line."); match the entire line.");
doc!(h, "regex-size-limit",
"The upper size limit of the compiled regex.",
"The upper size limit of the compiled regex. The default limit \
is 10M. \n\nThe argument accepts the same size suffixes as \
allowed in the 'max-filesize' argument.");
doc!(h, "case-sensitive", doc!(h, "case-sensitive",
"Search case sensitively.", "Search case sensitively.",
"Search case sensitively. This overrides -i/--ignore-case and \ "Search case sensitively. This overrides -i/--ignore-case and \

View File

@@ -35,7 +35,6 @@ pub struct Args {
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
after_context: usize, after_context: usize,
before_context: usize, before_context: usize,
color: bool,
color_choice: termcolor::ColorChoice, color_choice: termcolor::ColorChoice,
colors: ColorSpecs, colors: ColorSpecs,
column: bool, column: bool,
@@ -317,7 +316,6 @@ impl<'a> ArgMatches<'a> {
paths: paths, paths: paths,
after_context: after_context, after_context: after_context,
before_context: before_context, before_context: before_context,
color: self.color(),
color_choice: self.color_choice(), color_choice: self.color_choice(),
colors: try!(self.color_specs()), colors: try!(self.color_specs()),
column: self.column(), column: self.column(),
@@ -377,7 +375,7 @@ impl<'a> ArgMatches<'a> {
if self.is_present("file") if self.is_present("file")
|| self.is_present("files") || self.is_present("files")
|| self.is_present("regexp") { || self.is_present("regexp") {
if let Some(path) = self.value_of_os("pattern") { if let Some(path) = self.value_of_os("PATTERN") {
paths.insert(0, Path::new(path).to_path_buf()); paths.insert(0, Path::new(path).to_path_buf());
} }
} }
@@ -429,7 +427,8 @@ impl<'a> ArgMatches<'a> {
/// ///
/// Note that if -F/--fixed-strings is set, then all patterns will be /// Note that if -F/--fixed-strings is set, then all patterns will be
/// escaped. Similarly, if -w/--word-regexp is set, then all patterns /// escaped. Similarly, if -w/--word-regexp is set, then all patterns
/// are surrounded by `\b`. /// are surrounded by `\b`, and if -x/--line-regexp is set, then all
/// patterns are surrounded by `^...$`.
/// ///
/// If any pattern is invalid UTF-8, then an error is returned. /// If any pattern is invalid UTF-8, then an error is returned.
fn patterns(&self) -> Result<Vec<String>> { fn patterns(&self) -> Result<Vec<String>> {
@@ -440,7 +439,7 @@ impl<'a> ArgMatches<'a> {
match self.values_of_os("regexp") { match self.values_of_os("regexp") {
None => { None => {
if self.values_of_os("file").is_none() { if self.values_of_os("file").is_none() {
if let Some(os_pat) = self.value_of_os("pattern") { if let Some(os_pat) = self.value_of_os("PATTERN") {
pats.push(try!(self.os_str_pattern(os_pat))); pats.push(try!(self.os_str_pattern(os_pat)));
} }
} }
@@ -472,7 +471,7 @@ impl<'a> ArgMatches<'a> {
Ok(pats) Ok(pats)
} }
/// Converts an OsStr pattern to a String pattern, including word /// Converts an OsStr pattern to a String pattern, including line/word
/// boundaries or escapes if applicable. /// boundaries or escapes if applicable.
/// ///
/// If the pattern is not valid UTF-8, then an error is returned. /// If the pattern is not valid UTF-8, then an error is returned.
@@ -481,10 +480,12 @@ impl<'a> ArgMatches<'a> {
Ok(self.str_pattern(s)) Ok(self.str_pattern(s))
} }
/// Converts a &str pattern to a String pattern, including word /// Converts a &str pattern to a String pattern, including line/word
/// boundaries or escapes if applicable. /// boundaries or escapes if applicable.
fn str_pattern(&self, pat: &str) -> String { fn str_pattern(&self, pat: &str) -> String {
let s = self.word_pattern(self.literal_pattern(pat.to_string())); let litpat = self.literal_pattern(pat.to_string());
let s = self.line_pattern(self.word_pattern(litpat));
if s.is_empty() { if s.is_empty() {
self.empty_pattern() self.empty_pattern()
} else { } else {
@@ -507,7 +508,17 @@ impl<'a> ArgMatches<'a> {
/// flag is set. Otherwise, the pattern is returned unchanged. /// flag is set. Otherwise, the pattern is returned unchanged.
fn word_pattern(&self, pat: String) -> String { fn word_pattern(&self, pat: String) -> String {
if self.is_present("word-regexp") { if self.is_present("word-regexp") {
format!(r"\b{}\b", pat) format!(r"\b(?:{})\b", pat)
} else {
pat
}
}
/// Returns the given pattern as a line pattern if the -x/--line-regexp
/// flag is set. Otherwise, the pattern is returned unchanged.
fn line_pattern(&self, pat: String) -> String {
if self.is_present("line-regexp") {
format!(r"^(?:{})$", pat)
} else { } else {
pat pat
} }
@@ -532,6 +543,7 @@ impl<'a> ArgMatches<'a> {
false false
} else { } else {
self.is_present("with-filename") self.is_present("with-filename")
|| self.is_present("vimgrep")
|| paths.len() > 1 || paths.len() > 1
|| paths.get(0).map_or(false, |p| p.is_dir()) || paths.get(0).map_or(false, |p| p.is_dir())
} }
@@ -590,9 +602,9 @@ impl<'a> ArgMatches<'a> {
false false
} else { } else {
let only_stdin = paths == &[Path::new("-")]; let only_stdin = paths == &[Path::new("-")];
self.is_present("line-number") (atty::is(atty::Stream::Stdout) && !only_stdin)
|| self.is_present("line-number")
|| self.is_present("column") || self.is_present("column")
|| (atty::is(atty::Stream::Stdout) && !only_stdin)
|| self.is_present("pretty") || self.is_present("pretty")
|| self.is_present("vimgrep") || self.is_present("vimgrep")
} }
@@ -606,11 +618,11 @@ impl<'a> ArgMatches<'a> {
/// Returns true if and only if matches should be grouped with file name /// Returns true if and only if matches should be grouped with file name
/// headings. /// headings.
fn heading(&self) -> bool { fn heading(&self) -> bool {
if self.is_present("no-heading") { if self.is_present("no-heading") || self.is_present("vimgrep") {
false false
} else { } else {
self.is_present("heading") atty::is(atty::Stream::Stdout)
|| atty::is(atty::Stream::Stdout) || self.is_present("heading")
|| self.is_present("pretty") || self.is_present("pretty")
} }
} }
@@ -664,23 +676,6 @@ impl<'a> ArgMatches<'a> {
}) })
} }
/// Returns true if and only if ripgrep should color its output.
fn color(&self) -> bool {
let preference = match self.0.value_of_lossy("color") {
None => "auto".to_string(),
Some(v) => v.into_owned(),
};
if preference == "always" {
true
} else if self.is_present("vimgrep") {
false
} else if preference == "auto" {
atty::is(atty::Stream::Stdout) || self.is_present("pretty")
} else {
false
}
}
/// Returns the user's color choice based on command line parameters and /// Returns the user's color choice based on command line parameters and
/// environment. /// environment.
fn color_choice(&self) -> termcolor::ColorChoice { fn color_choice(&self) -> termcolor::ColorChoice {
@@ -692,8 +687,6 @@ impl<'a> ArgMatches<'a> {
termcolor::ColorChoice::Always termcolor::ColorChoice::Always
} else if preference == "ansi" { } else if preference == "ansi" {
termcolor::ColorChoice::AlwaysAnsi termcolor::ColorChoice::AlwaysAnsi
} else if self.is_present("vimgrep") {
termcolor::ColorChoice::Never
} else if preference == "auto" { } else if preference == "auto" {
if atty::is(atty::Stream::Stdout) || self.is_present("pretty") { if atty::is(atty::Stream::Stdout) || self.is_present("pretty") {
termcolor::ColorChoice::Auto termcolor::ColorChoice::Auto
@@ -712,7 +705,10 @@ impl<'a> ArgMatches<'a> {
fn color_specs(&self) -> Result<ColorSpecs> { fn color_specs(&self) -> Result<ColorSpecs> {
// Start with a default set of color specs. // Start with a default set of color specs.
let mut specs = vec![ let mut specs = vec![
#[cfg(unix)]
"path:fg:magenta".parse().unwrap(), "path:fg:magenta".parse().unwrap(),
#[cfg(windows)]
"path:fg:cyan".parse().unwrap(),
"line:fg:green".parse().unwrap(), "line:fg:green".parse().unwrap(),
"match:fg:red".parse().unwrap(), "match:fg:red".parse().unwrap(),
"match:style:bold".parse().unwrap(), "match:style:bold".parse().unwrap(),
@@ -737,7 +733,7 @@ impl<'a> ArgMatches<'a> {
if label == "auto" { if label == "auto" {
return Ok(None); return Ok(None);
} }
match Encoding::for_label(label.as_bytes()) { match Encoding::for_label_no_replacement(label.as_bytes()) {
Some(enc) => Ok(Some(enc)), Some(enc) => Ok(Some(enc)),
None => Err(From::from( None => Err(From::from(
format!("unsupported encoding: {}", label))), format!("unsupported encoding: {}", label))),
@@ -771,12 +767,18 @@ impl<'a> ArgMatches<'a> {
let casei = let casei =
self.is_present("ignore-case") self.is_present("ignore-case")
&& !self.is_present("case-sensitive"); && !self.is_present("case-sensitive");
GrepBuilder::new(&try!(self.pattern())) let mut gb = GrepBuilder::new(&try!(self.pattern()))
.case_smart(smart) .case_smart(smart)
.case_insensitive(casei) .case_insensitive(casei)
.line_terminator(b'\n') .line_terminator(b'\n');
.build()
.map_err(From::from) if let Some(limit) = try!(self.dfa_size_limit()) {
gb = gb.dfa_size_limit(limit);
}
if let Some(limit) = try!(self.regex_size_limit()) {
gb = gb.size_limit(limit);
}
gb.build().map_err(From::from)
} }
/// Builds the set of glob overrides from the command line flags. /// Builds the set of glob overrides from the command line flags.
@@ -785,6 +787,14 @@ impl<'a> ArgMatches<'a> {
for glob in self.values_of_lossy_vec("glob") { for glob in self.values_of_lossy_vec("glob") {
try!(ovr.add(&glob)); try!(ovr.add(&glob));
} }
// this is smelly. In the long run it might make sense
// to change overridebuilder to be like globsetbuilder
// but this would be a breaking change to the ignore crate
// so it is being shelved for now...
try!(ovr.case_insensitive(true));
for glob in self.values_of_lossy_vec("iglob") {
try!(ovr.add(&glob));
}
ovr.build().map_err(From::from) ovr.build().map_err(From::from)
} }
@@ -807,31 +817,64 @@ impl<'a> ArgMatches<'a> {
btypes.build().map_err(From::from) btypes.build().map_err(From::from)
} }
/// Parses the max-filesize argument option into a byte count. /// Parses an argument of the form `[0-9]+(KMG)?`.
fn max_filesize(&self) -> Result<Option<u64>> { ///
use regex::Regex; /// This always returns the result as a type `u64`. This must be converted
/// to the appropriate type by the caller.
let max_filesize = match self.value_of_lossy("max-filesize") { fn parse_human_readable_size_arg(
&self,
arg_name: &str,
) -> Result<Option<u64>> {
let arg_value = match self.value_of_lossy(arg_name) {
Some(x) => x, Some(x) => x,
None => return Ok(None) None => return Ok(None)
}; };
let re = regex::Regex::new("^([0-9]+)([KMG])?$").unwrap();
let caps = try!(
re.captures(&arg_value).ok_or_else(|| {
format!("invalid format for {}", arg_name)
}));
let re = Regex::new("^([0-9]+)([KMG])?$").unwrap(); let value = try!(caps[1].parse::<u64>());
let caps = try!(re.captures(&max_filesize)
.ok_or("invalid format for max-filesize argument"));
let value = try!(caps[1].parse::<u64>().map_err(|err|err.to_string()));
let suffix = caps.get(2).map(|x| x.as_str()); let suffix = caps.get(2).map(|x| x.as_str());
let v_10 = value.checked_mul(1024);
let v_20 = v_10.and_then(|x| x.checked_mul(1024));
let v_30 = v_20.and_then(|x| x.checked_mul(1024));
let try_suffix = |x: Option<u64>| {
if x.is_some() {
Ok(x)
} else {
Err(From::from(format!("number too large for {}", arg_name)))
}
};
match suffix { match suffix {
None => Ok(Some(value)), None => Ok(Some(value)),
Some("K") => Ok(Some(value * 1024)), Some("K") => try_suffix(v_10),
Some("M") => Ok(Some(value * 1024 * 1024)), Some("M") => try_suffix(v_20),
Some("G") => Ok(Some(value * 1024 * 1024 * 1024)), Some("G") => try_suffix(v_30),
_ => Err(From::from("invalid suffix for max-filesize argument")) _ => Err(From::from(format!("invalid suffix for {}", arg_name)))
} }
} }
/// Parse the dfa-size-limit argument option into a byte count.
fn dfa_size_limit(&self) -> Result<Option<usize>> {
let r = try!(self.parse_human_readable_size_arg("dfa-size-limit"));
human_readable_to_usize("dfa-size-limit", r)
}
/// Parse the regex-size-limit argument option into a byte count.
fn regex_size_limit(&self) -> Result<Option<usize>> {
let r = try!(self.parse_human_readable_size_arg("regex-size-limit"));
human_readable_to_usize("regex-size-limit", r)
}
/// Parses the max-filesize argument option into a byte count.
fn max_filesize(&self) -> Result<Option<u64>> {
self.parse_human_readable_size_arg("max-filesize")
}
/// Returns true if ignore files should be ignored. /// Returns true if ignore files should be ignored.
fn no_ignore(&self) -> bool { fn no_ignore(&self) -> bool {
self.is_present("no-ignore") self.is_present("no-ignore")
@@ -926,6 +969,27 @@ impl QuietMatched {
} }
} }
/// Convert the result of a `parse_human_readable_size_arg` call into
/// a `usize`, failing if the type does not fit.
fn human_readable_to_usize(
arg_name: &str,
value: Option<u64>,
) -> Result<Option<usize>> {
use std::usize;
match value {
None => Ok(None),
Some(v) => {
if v <= usize::MAX as u64 {
Ok(Some(v as usize))
} else {
let msg = format!("number too large for {}", arg_name);
Err(From::from(msg))
}
}
}
}
/// Returns true if and only if stdin is deemed searchable. /// Returns true if and only if stdin is deemed searchable.
#[cfg(unix)] #[cfg(unix)]
fn stdin_is_readable() -> bool { fn stdin_is_readable() -> bool {

View File

@@ -8,7 +8,6 @@ extern crate grep;
extern crate ignore; extern crate ignore;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate libc;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate memchr; extern crate memchr;
@@ -192,7 +191,9 @@ fn run_files_parallel(args: Arc<Args>) -> Result<u64> {
let mut printer = print_args.printer(stdout.lock()); let mut printer = print_args.printer(stdout.lock());
let mut file_count = 0; let mut file_count = 0;
for dent in rx.iter() { for dent in rx.iter() {
printer.path(dent.path()); if !print_args.quiet() {
printer.path(dent.path());
}
file_count += 1; file_count += 1;
} }
file_count file_count
@@ -227,7 +228,9 @@ fn run_files_one_thread(args: Arc<Args>) -> Result<u64> {
None => continue, None => continue,
Some(dent) => dent, Some(dent) => dent,
}; };
printer.path(dent.path()); if !args.quiet() {
printer.path(dent.path());
}
file_count += 1; file_count += 1;
} }
Ok(file_count) Ok(file_count)

View File

@@ -3,7 +3,7 @@ use std::fmt;
use std::path::Path; use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use regex::bytes::{Regex, Replacer, Captures}; use regex::bytes::{Captures, Regex, Replacer};
use termcolor::{Color, ColorSpec, ParseColorError, WriteColor}; use termcolor::{Color, ColorSpec, ParseColorError, WriteColor};
use pathutil::strip_prefix; use pathutil::strip_prefix;
@@ -242,25 +242,17 @@ impl<W: WriteColor> Printer<W> {
line_number: Option<u64>, line_number: Option<u64>,
) { ) {
if !self.line_per_match && !self.only_matching { if !self.line_per_match && !self.only_matching {
let column = let mat = re
if self.column { .find(&buf[start..end])
Some(re.find(&buf[start..end]) .map(|m| (m.start(), m.end()))
.map(|m| m.start()).unwrap_or(0) as u64) .unwrap_or((0, 0));
} else {
None
};
return self.write_match( return self.write_match(
re, path, buf, start, end, line_number, column); re, path, buf, start, end, line_number, mat.0, mat.1);
} }
for m in re.find_iter(&buf[start..end]) { for m in re.find_iter(&buf[start..end]) {
let column =
if self.column {
Some(m.start() as u64)
} else {
None
};
self.write_match( self.write_match(
re, path.as_ref(), buf, start, end, line_number, column); re, path.as_ref(), buf, start, end,
line_number, m.start(), m.end());
} }
} }
@@ -272,7 +264,8 @@ impl<W: WriteColor> Printer<W> {
start: usize, start: usize,
end: usize, end: usize,
line_number: Option<u64>, line_number: Option<u64>,
column: Option<u64>, match_start: usize,
match_end: usize,
) { ) {
if self.heading && self.with_filename && !self.has_printed { if self.heading && self.with_filename && !self.has_printed {
self.write_file_sep(); self.write_file_sep();
@@ -285,8 +278,8 @@ impl<W: WriteColor> Printer<W> {
if let Some(line_number) = line_number { if let Some(line_number) = line_number {
self.line_number(line_number, b':'); self.line_number(line_number, b':');
} }
if let Some(c) = column { if self.column {
self.column_number(c + 1, b':'); self.column_number(match_start as u64 + 1, b':');
} }
if self.replace.is_some() { if self.replace.is_some() {
let mut count = 0; let mut count = 0;
@@ -307,18 +300,21 @@ impl<W: WriteColor> Printer<W> {
self.write_eol(); self.write_eol();
} }
} else { } else {
let line_buf = if self.only_matching { if self.only_matching {
let m = re.find(&buf[start..end]).unwrap(); let buf = &buf[start + match_start..start + match_end];
&buf[start + m.start()..start + m.end()] self.write_matched_line(re, buf, true);
} else { } else {
&buf[start..end] self.write_matched_line(re, &buf[start..end], false);
}; }
self.write_matched_line(re, line_buf);
// write_matched_line guarantees to write a newline.
} }
} }
fn write_matched_line(&mut self, re: &Regex, buf: &[u8]) { fn write_matched_line(
&mut self,
re: &Regex,
buf: &[u8],
only_match: bool,
) {
if self.max_columns.map_or(false, |m| buf.len() > m) { if self.max_columns.map_or(false, |m| buf.len() > m) {
let count = re.find_iter(buf).count(); let count = re.find_iter(buf).count();
let msg = format!("[Omitted long line with {} matches]", count); let msg = format!("[Omitted long line with {} matches]", count);
@@ -328,6 +324,8 @@ impl<W: WriteColor> Printer<W> {
} }
if !self.wtr.supports_color() || self.colors.matched().is_none() { if !self.wtr.supports_color() || self.colors.matched().is_none() {
self.write(buf); self.write(buf);
} else if only_match {
self.write_colored(buf, |colors| colors.matched());
} else { } else {
let mut last_written = 0; let mut last_written = 0;
for m in re.find_iter(buf) { for m in re.find_iter(buf) {
@@ -578,7 +576,7 @@ pub struct ColorSpecs {
/// Valid colors are `black`, `blue`, `green`, `red`, `cyan`, `magenta`, /// Valid colors are `black`, `blue`, `green`, `red`, `cyan`, `magenta`,
/// `yellow`, `white`. /// `yellow`, `white`.
/// ///
/// Valid style instructions are `nobold` and `bold`. /// Valid style instructions are `nobold`, `bold`, `intense`, `nointense`.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct Spec { pub struct Spec {
ty: OutType, ty: OutType,

View File

@@ -209,6 +209,16 @@ For the Doctor Watsons of this world, as opposed to the Sherlock
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
sherlock!(line, "Watson|and exhibited clearly, with a label attached.",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("-x");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
and exhibited clearly, with a label attached.
";
assert_eq!(lines, expected);
});
sherlock!(literal, "()", "file", |wd: WorkDir, mut cmd: Command| { sherlock!(literal, "()", "file", |wd: WorkDir, mut cmd: Command| {
wd.create("file", "blib\n()\nblab\n"); wd.create("file", "blib\n()\nblab\n");
cmd.arg("-F"); cmd.arg("-F");
@@ -336,6 +346,21 @@ sherlock!(glob_negate, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
assert_eq!(lines, "file.py:Sherlock\n"); assert_eq!(lines, "file.py:Sherlock\n");
}); });
sherlock!(iglob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
wd.create("file.HTML", "Sherlock");
cmd.arg("--iglob").arg("*.html");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "file.HTML:Sherlock\n");
});
sherlock!(csglob, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
wd.create("file1.HTML", "Sherlock");
wd.create("file2.html", "Sherlock");
cmd.arg("--glob").arg("*.html");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "file2.html:Sherlock\n");
});
sherlock!(count, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { sherlock!(count, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
cmd.arg("--count"); cmd.arg("--count");
let lines: String = wd.stdout(&mut cmd); let lines: String = wd.stdout(&mut cmd);
@@ -455,7 +480,6 @@ sherlock!(max_filesize_parse_no_suffix, "Sherlock", ".",
let expected = "\ let expected = "\
foo foo
"; ";
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
@@ -470,7 +494,6 @@ sherlock!(max_filesize_parse_k_suffix, "Sherlock", ".",
let expected = "\ let expected = "\
foo foo
"; ";
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
@@ -485,10 +508,19 @@ sherlock!(max_filesize_parse_m_suffix, "Sherlock", ".",
let expected = "\ let expected = "\
foo foo
"; ";
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
sherlock!(max_filesize_suffix_overflow, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
wd.remove("sherlock");
wd.create_size("foo", 1000000);
// 2^35 * 2^30 would otherwise overflow
cmd.arg("--max-filesize").arg("34359738368G").arg("--files");
wd.assert_err(&mut cmd);
});
sherlock!(ignore_hidden, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { sherlock!(ignore_hidden, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
wd.remove("sherlock"); wd.remove("sherlock");
wd.create(".sherlock", hay::SHERLOCK); wd.create(".sherlock", hay::SHERLOCK);
@@ -1058,21 +1090,25 @@ clean!(regression_405, "test", ".", |wd: WorkDir, mut cmd: Command| {
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/428 // See: https://github.com/BurntSushi/ripgrep/issues/428
clean!(regression_428_color_context_path, "foo", ".", |wd: WorkDir, mut cmd: Command| { #[cfg(not(windows))]
clean!(regression_428_color_context_path, "foo", ".",
|wd: WorkDir, mut cmd: Command| {
wd.create("sherlock", "foo\nbar"); wd.create("sherlock", "foo\nbar");
cmd.arg("-A1").arg("-H").arg("--no-heading").arg("-N") cmd.arg("-A1").arg("-H").arg("--no-heading").arg("-N")
.arg("--colors=match:none").arg("--color=always"); .arg("--colors=match:none").arg("--color=always");
let lines: String = wd.stdout(&mut cmd); let lines: String = wd.stdout(&mut cmd);
let expected = format!("\ let expected = format!(
{colored_path}:foo "{colored_path}:foo\n{colored_path}-bar\n",
{colored_path}-bar colored_path=format!(
", colored_path=format!("\x1b\x5b\x6d\x1b\x5b\x33\x35\x6d{path}\x1b\x5b\x6d", path=path("sherlock"))); "\x1b\x5b\x6d\x1b\x5b\x33\x35\x6d{path}\x1b\x5b\x6d",
path=path("sherlock")));
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/428 // See: https://github.com/BurntSushi/ripgrep/issues/428
clean!(regression_428_unrecognized_style, "Sherlok", ".", |wd: WorkDir, mut cmd: Command| { clean!(regression_428_unrecognized_style, "Sherlok", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--colors=match:style:"); cmd.arg("--colors=match:style:");
wd.assert_err(&mut cmd); wd.assert_err(&mut cmd);
@@ -1084,6 +1120,15 @@ Unrecognized style attribute ''. Choose from: nobold, bold, nointense, intense.
assert_eq!(err, expected); assert_eq!(err, expected);
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/493
clean!(regression_493, " 're ", "input.txt", |wd: WorkDir, mut cmd: Command| {
wd.create("input.txt", "peshwaship 're seminomata");
cmd.arg("-o").arg("-w");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, " 're \n");
});
// See: https://github.com/BurntSushi/ripgrep/issues/1 // See: https://github.com/BurntSushi/ripgrep/issues/1
clean!(feature_1_sjis, "Шерлок Холмс", ".", |wd: WorkDir, mut cmd: Command| { clean!(feature_1_sjis, "Шерлок Холмс", ".", |wd: WorkDir, mut cmd: Command| {
let sherlock = let sherlock =
@@ -1130,6 +1175,21 @@ clean!(feature_1_eucjp, "Шерлок Холмс", ".",
assert_eq!(lines, "foo:Шерлок Холмс\n"); assert_eq!(lines, "foo:Шерлок Холмс\n");
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/1
sherlock!(feature_1_unknown_encoding, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("-Efoobar");
wd.assert_non_empty_stderr(&mut cmd);
});
// See: https://github.com/BurntSushi/ripgrep/issues/1
// Specific: https://github.com/BurntSushi/ripgrep/pull/398/files#r111109265
sherlock!(feature_1_replacement_encoding, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("-Ecsiso2022kr");
wd.assert_non_empty_stderr(&mut cmd);
});
// See: https://github.com/BurntSushi/ripgrep/issues/7 // See: https://github.com/BurntSushi/ripgrep/issues/7
sherlock!(feature_7, "-fpat", "sherlock", |wd: WorkDir, mut cmd: Command| { sherlock!(feature_7, "-fpat", "sherlock", |wd: WorkDir, mut cmd: Command| {
wd.create("pat", "Sherlock\nHolmes"); wd.create("pat", "Sherlock\nHolmes");
@@ -1443,6 +1503,37 @@ clean!(feature_275_pathsep, "test", ".", |wd: WorkDir, mut cmd: Command| {
assert_eq!(lines, "fooZbar:test\n"); assert_eq!(lines, "fooZbar:test\n");
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/362
sherlock!(feature_362_dfa_size_limit, r"For\s",
|wd: WorkDir, mut cmd: Command| {
// This should fall back to the nfa engine but should still produce the
// expected result.
cmd.arg("--dfa-size-limit").arg("10");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
For the Doctor Watsons of this world, as opposed to the Sherlock
";
assert_eq!(lines, expected);
});
sherlock!(feature_362_exceeds_regex_size_limit, r"[0-9]\w+",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--regex-size-limit").arg("10K");
wd.assert_err(&mut cmd);
});
#[cfg(target_pointer_width = "32")]
sherlock!(feature_362_u64_to_narrow_usize_suffix_overflow, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
wd.remove("sherlock");
wd.create_size("foo", 1000000);
// 2^35 * 2^20 is ok for u64, but not for usize
cmd.arg("--dfa-size-limit").arg("34359738368M").arg("--files");
wd.assert_err(&mut cmd);
});
// See: https://github.com/BurntSushi/ripgrep/issues/419 // See: https://github.com/BurntSushi/ripgrep/issues/419
sherlock!(feature_419_zero_as_shortcut_for_null, "Sherlock", ".", sherlock!(feature_419_zero_as_shortcut_for_null, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| { |wd: WorkDir, mut cmd: Command| {
@@ -1554,6 +1645,120 @@ fn regression_391() {
assert_eq!(lines, "bar.py\n"); assert_eq!(lines, "bar.py\n");
} }
// See: https://github.com/BurntSushi/ripgrep/issues/451
#[test]
fn regression_451_only_matching_as_in_issue() {
let wd = WorkDir::new("regression_451_only_matching");
let path = "digits.txt";
wd.create(path, "1 2 3\n");
let mut cmd = wd.command();
cmd.arg("[0-9]+").arg(path).arg("--only-matching");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
1
2
3
";
assert_eq!(lines, expected);
}
// See: https://github.com/BurntSushi/ripgrep/issues/451
#[test]
fn regression_451_only_matching() {
let wd = WorkDir::new("regression_451_only_matching");
let path = "digits.txt";
wd.create(path, "1 2 3\n123\n");
let mut cmd = wd.command();
cmd.arg("[0-9]").arg(path)
.arg("--only-matching")
.arg("--column");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
1:1:1
1:3:2
1:5:3
2:1:1
2:2:2
2:3:3
";
assert_eq!(lines, expected);
}
// See: https://github.com/BurntSushi/ripgrep/issues/483
#[test]
fn regression_483_matching_no_stdout() {
let wd = WorkDir::new("regression_483_matching_no_stdout");
wd.create("file.py", "");
let mut cmd = wd.command();
cmd.arg("--quiet")
.arg("--files")
.arg("--glob").arg("*.py");
let lines: String = wd.stdout(&mut cmd);
assert!(lines.is_empty());
}
// See: https://github.com/BurntSushi/ripgrep/issues/483
#[test]
fn regression_483_non_matching_exit_code() {
let wd = WorkDir::new("regression_483_non_matching_exit_code");
wd.create("file.rs", "");
let mut cmd = wd.command();
cmd.arg("--quiet")
.arg("--files")
.arg("--glob").arg("*.py");
wd.assert_err(&mut cmd);
}
// See: https://github.com/BurntSushi/ripgrep/issues/506
#[test]
fn regression_506_word_boundaries_not_parenthesized() {
let wd = WorkDir::new("regression_506_word_boundaries_not_parenthesized");
let path = "wb.txt";
wd.create(path, "min minimum amin\n\
max maximum amax");
let mut cmd = wd.command();
cmd.arg("-w").arg("min|max").arg(path).arg("--only-matching");
let lines: String = wd.stdout(&mut cmd);
let expected = "min\nmax\n";
assert_eq!(lines, expected);
}
// See: https://github.com/BurntSushi/ripgrep/issues/568
#[test]
fn regression_568_leading_hyphen_option_arguments() {
let wd = WorkDir::new("regression_568_leading_hyphen_option_arguments");
let path = "file";
wd.create(path, "foo bar -baz\n");
let mut cmd = wd.command();
cmd.arg("-e-baz").arg("-e").arg("-baz").arg(path);
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "foo bar -baz\n");
let mut cmd = wd.command();
cmd.arg("-rni").arg("bar").arg(path);
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "foo ni -baz\n");
let mut cmd = wd.command();
cmd.arg("-r").arg("-n").arg("-i").arg("bar").arg(path);
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "foo -n -baz\n");
}
#[test] #[test]
fn type_list() { fn type_list() {
let wd = WorkDir::new("type_list"); let wd = WorkDir::new("type_list");

View File

@@ -256,6 +256,23 @@ impl WorkDir {
String::from_utf8_lossy(&o.stderr)); String::from_utf8_lossy(&o.stderr));
} }
} }
/// Runs the given command and asserts that something was printed to
/// stderr.
pub fn assert_non_empty_stderr(&self, cmd: &mut process::Command) {
let o = cmd.output().unwrap();
if o.status.success() || o.stderr.is_empty() {
panic!("\n\n===== {:?} =====\n\
command succeeded but expected failure!\
\n\ncwd: {}\
\n\nstatus: {}\
\n\nstdout: {}\n\nstderr: {}\
\n\n=====\n",
cmd, self.dir.display(), o.status,
String::from_utf8_lossy(&o.stdout),
String::from_utf8_lossy(&o.stderr));
}
}
} }
fn nice_err<P: AsRef<Path>, T, E: error::Error>( fn nice_err<P: AsRef<Path>, T, E: error::Error>(

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "wincolor" name = "wincolor"
version = "0.1.3" #:version version = "0.1.4" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"] authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """ description = """
A simple Windows specific API for controlling text color in a Windows console. A simple Windows specific API for controlling text color in a Windows console.

View File

@@ -2,7 +2,7 @@ use std::io;
use std::mem; use std::mem;
use kernel32; use kernel32;
use winapi::{DWORD, HANDLE, WORD}; use winapi::{DWORD, WORD};
use winapi::winbase::{STD_ERROR_HANDLE, STD_OUTPUT_HANDLE}; use winapi::winbase::{STD_ERROR_HANDLE, STD_OUTPUT_HANDLE};
use winapi::wincon::{ use winapi::wincon::{
FOREGROUND_BLUE as FG_BLUE, FOREGROUND_BLUE as FG_BLUE,
@@ -30,33 +30,25 @@ const FG_WHITE: DWORD = FG_BLUE | FG_GREEN | FG_RED;
/// stdout before setting new text attributes. /// stdout before setting new text attributes.
#[derive(Debug)] #[derive(Debug)]
pub struct Console { pub struct Console {
handle: HANDLE, handle_id: DWORD,
start_attr: TextAttributes, start_attr: TextAttributes,
cur_attr: TextAttributes, cur_attr: TextAttributes,
} }
unsafe impl Send for Console {}
impl Drop for Console {
fn drop(&mut self) {
unsafe { kernel32::CloseHandle(self.handle); }
}
}
impl Console { impl Console {
/// Get a console for a standard I/O stream. /// Get a console for a standard I/O stream.
fn create_for_stream(handle_id: DWORD) -> io::Result<Console> { fn create_for_stream(handle_id: DWORD) -> io::Result<Console> {
let mut info = unsafe { mem::zeroed() }; let mut info = unsafe { mem::zeroed() };
let (handle, res) = unsafe { let res = unsafe {
let handle = kernel32::GetStdHandle(handle_id); let handle = kernel32::GetStdHandle(handle_id);
(handle, kernel32::GetConsoleScreenBufferInfo(handle, &mut info)) kernel32::GetConsoleScreenBufferInfo(handle, &mut info)
}; };
if res == 0 { if res == 0 {
return Err(io::Error::last_os_error()); return Err(io::Error::last_os_error());
} }
let attr = TextAttributes::from_word(info.wAttributes); let attr = TextAttributes::from_word(info.wAttributes);
Ok(Console { Ok(Console {
handle: handle, handle_id: handle_id,
start_attr: attr, start_attr: attr,
cur_attr: attr, cur_attr: attr,
}) })
@@ -80,7 +72,8 @@ impl Console {
fn set(&mut self) -> io::Result<()> { fn set(&mut self) -> io::Result<()> {
let attr = self.cur_attr.to_word(); let attr = self.cur_attr.to_word();
let res = unsafe { let res = unsafe {
kernel32::SetConsoleTextAttribute(self.handle, attr) let handle = kernel32::GetStdHandle(self.handle_id);
kernel32::SetConsoleTextAttribute(handle, attr)
}; };
if res == 0 { if res == 0 {
return Err(io::Error::last_os_error()); return Err(io::Error::last_os_error());