Compare commits

...

40 Commits

Author SHA1 Message Date
Andrew Gallant
fd9870d668 Revert "Add _rg.ps1 to windows zip"
This reverts commit d570f78144.

This was reverted because it's blocking a 0.5.0 release. Windows is
foreign to me, and apparently globs are not allowed.

See:
https://ci.appveyor.com/project/BurntSushi/ripgrep/build/1.0.341/job/7o1jqicmtwm7oa00
2017-03-12 22:59:37 -04:00
Andrew Gallant
a3a2708067 update brew tap to 0.5.0 2017-03-12 22:55:59 -04:00
Andrew Gallant
78847b65c8 0.5.0 2017-03-12 22:32:43 -04:00
Andrew Gallant
e962eea1cc add date to CHANGELOG 2017-03-12 22:31:57 -04:00
Andrew Gallant
95bc678403 Fix interaction with clap.
Previously, `get_matches` would return even if --help or --version was
given, and we could check for them manually. That behavior seems to have
changed. Instead, we must use get_matches_safe to inspect the error to
determine what happened.

We can't use the same process for -V/--version since clap will
unconditionally print its own version info. Instead, we rename (internally)
the version flag so that clap doesn't interfere.
2017-03-12 22:30:54 -04:00
Andrew Gallant
68af3bbdc4 fix CHANGELOG link 2017-03-12 21:58:29 -04:00
Andrew Gallant
70b6bdb104 changelog 0.5.0 2017-03-12 21:57:50 -04:00
Andrew Gallant
c648eadbaa Bump and update deps. 2017-03-12 21:33:13 -04:00
Ralf Jung
d352b79294 Add new -M/--max-columns option.
This permits setting the maximum line width with respect to the number
of bytes in a line. Omitted lines (whether part of a match, replacement
or context) are replaced with a message stating that the line was
elided.

Fixes #129
2017-03-12 21:21:28 -04:00
Andrew Gallant
23aec58669 pin nightly 2017-03-12 20:52:28 -04:00
Andrew Gallant
ae863bc7aa Improve docs for --glob flag.
Fixes #345.
2017-03-12 20:31:09 -04:00
Andrew Gallant
f0d3cae569 Clarify -u/--unrestricted flags.
Fixes #340
2017-03-12 20:24:45 -04:00
Andrew Gallant
4ef4818130 No line numbers when searching only stdin.
This changes the default behavior of ripgrep to *not* show line numbers
when it is printing to a tty and is only searching stdin.

Fixes #380

[breaking-change]
2017-03-12 20:21:40 -04:00
Andrew Gallant
8db24e1353 Stop aggressive inlining.
It's not clear what exactly is happening here, but the Read implementation
for text decoding appears a bit sensitive. Small pertubations in the code
appear to have a nearly 100% impact on the overall speed of ripgrep when
searching UTF-16 files.

I haven't had the time to examine the generated code in detail, but
`perf stat` seems to think that the instruction cache is performing a lot
worse when the code slows down. This might mean that excessive inlining
causes a different code structure that leads to less-than-optimal icache
usage, but it's at best a guess.

Explicitly disabling the inline for the cold path seems to help the
optimizer figure out the right thing.
2017-03-12 20:21:22 -04:00
Andrew Gallant
8bbe58d623 Add support for additional text encodings.
This includes, but is not limited to, UTF-16, latin-1, GBK, EUC-JP and
Shift_JIS. (Courtesy of the `encoding_rs` crate.)

Specifically, this feature enables ripgrep to search files that are
encoded in an encoding other than UTF-8. The list of available encodings
is tied directly to what the `encoding_rs` crate supports, which is in
turn tied to the Encoding Standard. The full list of available encodings
can be found here: https://encoding.spec.whatwg.org/#concept-encoding-get

This pull request also introduces the notion that text encodings can be
automatically detected on a best effort basis. Currently, the only
support for this is checking for a UTF-16 bom. In all other cases, a
text encoding of `auto` (the default) implies a UTF-8 or ASCII
compatible source encoding. When a text encoding is otherwise specified,
it is unconditionally used for all files searched.

Since ripgrep's regex engine is fundamentally built on top of UTF-8,
this feature works by transcoding the files to be searched from their
source encoding to UTF-8. This transcoding only happens when:

1. `auto` is specified and a non-UTF-8 encoding is detected.
2. A specific encoding is given by end users (including UTF-8).

When transcoding occurs, errors are handled by automatically inserting
the Unicode replacement character. In this case, ripgrep's output is
guaranteed to be valid UTF-8 (excluding non-UTF-8 file paths, if they
are printed).

In all other cases, the source text is searched directly, which implies
an assumption that it is at least ASCII compatible, but where UTF-8 is
most useful. In this scenario, encoding errors are not detected. In this
case, ripgrep's output will match the input exactly, byte-for-byte.

This design may not be optimal in all cases, but it has some advantages:

1. In the happy path ("UTF-8 everywhere") remains happy. I have not been
   able to witness any performance regressions.
2. In the non-UTF-8 path, implementation complexity is kept relatively
   low. The cost here is transcoding itself. A potentially superior
   implementation might build decoding of any encoding into the regex
   engine itself. In particular, the fundamental problem with
   transcoding everything first is that literal optimizations are nearly
   negated.

Future work should entail improving the user experience. For example, we
might want to auto-detect more text encodings. A more elaborate UX
experience might permit end users to specify multiple text encodings,
although this seems hard to pull off in an ergonomic way.

Fixes #1
2017-03-12 19:54:48 -04:00
Joshua Horwitz
b3fd0df94b Fixes #394 - Added in svg to the types file 2017-03-12 19:52:01 -04:00
Andrew Gallant
c1b841e934 Add license files to each crate.
Fixes #381
2017-03-12 16:57:15 -04:00
Andrew Gallant
f5ede0e319 Add scss and ejs.
We add scss to the existing `css` file type and `ejs` to the existing
`html` file type.

Fixes #393
2017-03-12 16:51:55 -04:00
Andrew Gallant
6ecffec537 Fix test on Windows.
(This is what I get for directly pushing to master.)
2017-03-12 16:07:31 -04:00
Andrew Gallant
80e91a1f1d Fix leading slash bug when used with !.
When writing paths like `!/foo` in gitignore files (or when using the
-g/--glob flag), the presence of `!` would prevent the gitignore builder
from noticing the leading slash, which causes absolute path matching to
fail.

Fixes #405
2017-03-12 15:51:17 -04:00
Daniel Santa Cruz
d570f78144 Add _rg.ps1 to windows zip
Tested with local cargo build paths.
2017-03-09 09:45:28 -05:00
Andrew Gallant
7c37065911 update deps 2017-03-08 20:23:12 -05:00
Jean-Marie Comets
50f7a60a8d Add "Known issues" section in README.md
Also document that ctrl-c doesn't restore the termcolor.
Fixes #347.
2017-03-08 10:18:19 -05:00
Marc Tiehuis
33ec988d70 Remove regex build-dependency in Cargo.toml 2017-03-08 10:17:18 -05:00
Marc Tiehuis
adff43fbb4 Remove clap validator + add max-filesize integration tests 2017-03-08 10:17:18 -05:00
Marc Tiehuis
71585f6d47 Reduce unnecessary stat calls for max_filesize 2017-03-08 10:17:18 -05:00
tiehuis
714ae82241 Add --max-filesize option to cli
The --max-filesize option allows filtering files which are larger than
the specified limit. This is potentially useful if one is attempting to
search a number of large files without common file-types/suffixes.

See #369.
2017-03-08 10:17:18 -05:00
tiehuis
49fd668712 Add file size exclusion to walker
A maximum filesize can be specified as an argument to a `WalkBuilder`.
If a file exceeds the specified size it will be ignored as part of the
resulting file/directory set.

The filesize limit never applies to directories.
2017-03-08 10:17:18 -05:00
Marc Tiehuis
066f97d855 Add enclosing group to alternations in globs
Fixes #391.
2017-03-08 10:13:28 -05:00
David Salter
df1bf4a042 Added Chocolatey to the installation list 2017-03-01 06:41:52 -05:00
Andrew Gallant
4e8c0fc4ad bump clap to 2.20.5
Fixes #383
2017-02-25 18:43:13 -05:00
Igor Gnatenko
da1764dfd1 update env_logger to 0.4 2017-02-25 17:46:43 -05:00
Andrew Gallant
48a8a3a691 kick travis 2017-02-24 08:41:20 -05:00
deepy
796eaab0d7 Add .log as FileType 2017-02-23 11:41:32 -05:00
Andrew Gallant
bf49448e1e fix badges 2017-02-19 11:28:36 -05:00
Andrew Gallant
cffba53379 use termcolor 0.3, not 0.1 2017-02-19 11:27:41 -05:00
Andrew Gallant
79d40d0e20 Tweak how binary files are handled internally.
This commit fixes two issues. The first issue is that if a file contained
many NUL bytes without any LF bytes, then the InputBuffer would read the
entire file into memory. This is not typically a problem, but if you run
rg on /proc, then bad things can happen when reading virtual memory mapping
files. Arguably, such files should be ignored, but we should also try to
avoid exhausting memory too. We fix this by pushing the `-a/--text` flag
option down into InputBuffer, so that it knows to stop immediately if it
finds a NUL byte.

The other issue this fixes is that binary detection is now applied to every
buffer instead of just the first one. This helps avoid detecting too many
files as plain text if the first parts of a binary file happen to contain
no NUL bytes. This issue still persists somewhat in the memory map
searcher, since we probably don't want to search the entire file upfront
for NUL bytes before actually performing our search. Instead, we search the
first 10KB for now.

Fixes #52, Fixes #311
2017-02-18 16:20:21 -05:00
Andrew Gallant
525b278049 Don't parses regexes with --files.
When the --files flag is given, ripgrep would still try to parse some of
the positional arguments as regexes. Don't do that.

Fixes #326
2017-02-18 15:34:54 -05:00
Andrew Gallant
16de47920c Permit --heading to override --no-heading.
@kbknapp <3

Fixes #327
2017-02-18 15:25:08 -05:00
Andrew Gallant
a114b86063 update termcolor dep 2017-02-18 15:09:25 -05:00
41 changed files with 1470 additions and 185 deletions

View File

@@ -9,13 +9,13 @@ matrix:
# (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 rust: nightly-2017-03-13
env: TARGET=i686-unknown-linux-musl env: TARGET=i686-unknown-linux-musl
- os: linux - os: linux
rust: nightly rust: nightly-2017-03-13
env: TARGET=x86_64-unknown-linux-musl env: TARGET=x86_64-unknown-linux-musl
- os: osx - os: osx
rust: nightly rust: nightly-2017-03-13
env: TARGET=x86_64-apple-darwin env: TARGET=x86_64-apple-darwin
# Beta channel. # Beta channel.
- os: linux - os: linux
@@ -57,7 +57,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 condition: $TRAVIS_RUST_VERSION = nightly-2017-03-13
tags: true tags: true
branches: branches:

View File

@@ -1,3 +1,65 @@
0.5.0 (2017-03-12)
==================
This is a new minor version release of ripgrep that includes one minor breaking
change, bug fixes and several new features including support for text encodings
other than UTF-8.
A notable accomplishment with respect to Rust is that ripgrep proper now only
contains a single `unsafe` use (for accessing the contents of a memory map).
The **breaking change** is:
* [FEATURE #380](https://github.com/BurntSushi/ripgrep/issues/380):
Line numbers are now hidden by default when ripgrep is printing to a tty
**and** the only thing searched is stdin.
Feature enhancements:
* Added or improved file type filtering for Ceylon, CSS, Elixir, HTML, log,
SASS, SVG, Twig
* [FEATURE #1](https://github.com/BurntSushi/ripgrep/issues/1):
Add support for additional text encodings, including automatic detection for
UTF-16 via BOM sniffing. Explicit text encoding support with the
`-E/--encoding` flag was also added for latin-1, GBK, EUC-JP
and Shift_JIS, among others. The full list can be found here:
https://encoding.spec.whatwg.org/#concept-encoding-get
* [FEATURE #129](https://github.com/BurntSushi/ripgrep/issues/129):
Add a new `-M/--max-columns` flag that omits lines longer than the given
number of bytes. (Disabled by default!)
* [FEATURE #369](https://github.com/BurntSushi/ripgrep/issues/369):
A new flag, `--max-filesize`, was added for limiting searches to files with
a maximum file size.
Bug fixes:
* [BUG #52](https://github.com/BurntSushi/ripgrep/issues/52),
[BUG #311](https://github.com/BurntSushi/ripgrep/issues/311):
Tweak how binary files are detected and handled. (We are slightly less
conservative and will no longer use memory without bound.)
* [BUG #326](https://github.com/BurntSushi/ripgrep/issues/326):
When --files flag is given, we should never attempt to parse positional
arguments as regexes.
* [BUG #327](https://github.com/BurntSushi/ripgrep/issues/327):
Permit the --heading flag to override the --no-heading flag.
* [BUG #340](https://github.com/BurntSushi/ripgrep/pull/340):
Clarify that the `-u/--unrestricted` flags are aliases.
* [BUG #343](https://github.com/BurntSushi/ripgrep/pull/343):
Global git ignore config should use `$HOME/.config/git/ignore` and not
`$HOME/git/ignore`.
* [BUG #345](https://github.com/BurntSushi/ripgrep/pull/345):
Clarify docs for `-g/--glob` flag.
* [BUG #381](https://github.com/BurntSushi/ripgrep/issues/381):
Add license files to each sub-crate.
* [BUG #383](https://github.com/BurntSushi/ripgrep/issues/383):
Use latest version of clap (for argv parsing).
* [BUG #392](https://github.com/BurntSushi/ripgrep/issues/391):
Fix translation of set globs (e.g., `{foo,bar,quux}`) to regexes.
* [BUG #401](https://github.com/BurntSushi/ripgrep/pull/401):
Add PowerShell completion file to Windows release.
* [BUG #405](https://github.com/BurntSushi/ripgrep/issues/405):
Fix bug when excluding absolute paths with the `-g/--glob` flag.
0.4.0 0.4.0
===== =====
This is a new minor version release of ripgrep that includes a couple very This is a new minor version release of ripgrep that includes a couple very

169
Cargo.lock generated
View File

@@ -4,26 +4,25 @@ version = "0.4.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.6 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.19.3 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"grep 0.1.5", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ignore 0.1.7", "grep 0.1.6",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ignore 0.1.8",
"lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.20 (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.6 (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.0 (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.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.3.0 (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.1 (registry+https://github.com/rust-lang/crates.io-index)",
"same-file 0.1.2 (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.2.0", "termcolor 0.3.1",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.6.1" version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"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)",
@@ -40,13 +39,13 @@ 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.20 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (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.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]]
@@ -57,19 +56,24 @@ dependencies = [
"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)",
] ]
[[package]]
name = "cfg-if"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.19.3" version = "2.21.1"
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)",
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.5.2 (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.1 (registry+https://github.com/rust-lang/crates.io-index)", "term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.1.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.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -78,11 +82,19 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "env_logger" name = "encoding_rs"
version = "0.3.5" version = "0.5.0"
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.6 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "env_logger"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -92,30 +104,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "fs2" name = "fs2"
version = "0.3.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"
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.20 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (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.3" version = "0.1.4"
dependencies = [ dependencies = [
"aho-corasick 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "aho-corasick 0.6.2 (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.6 (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)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "grep" name = "grep"
version = "0.1.5" version = "0.1.6"
dependencies = [ dependencies = [
"log 0.3.6 (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)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.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.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -123,15 +135,15 @@ dependencies = [
[[package]] [[package]]
name = "ignore" name = "ignore"
version = "0.1.7" version = "0.1.8"
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.3", "globset 0.1.4",
"lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (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)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.3 (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)",
] ]
@@ -146,17 +158,17 @@ dependencies = [
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "0.2.2" version = "0.2.4"
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.20" version = "0.2.21"
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.6" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -164,26 +176,26 @@ 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.20 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "memmap" name = "memmap"
version = "0.5.0" 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.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "fs2 0.4.1 (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.20 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (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.2.1" version = "1.3.0"
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.20 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -191,11 +203,11 @@ name = "regex"
version = "0.2.1" version = "0.2.1"
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.1 (registry+https://github.com/rust-lang/crates.io-index)", "aho-corasick 0.6.2 (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.0 (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.2 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.3 (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)",
] ]
@@ -206,11 +218,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "0.1.2" version = "0.1.3"
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.20 (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)",
] ]
@@ -221,24 +232,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.5.2" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "term_size" name = "term_size"
version = "0.2.1" version = "0.2.3"
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.20 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (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 = "termcolor" name = "termcolor"
version = "0.2.0" version = "0.3.1"
dependencies = [ dependencies = [
"wincolor 0.1.2", "wincolor 0.1.3",
] ]
[[package]] [[package]]
@@ -247,12 +258,12 @@ version = "3.0.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.20 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "0.3.2" version = "0.3.3"
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)", "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -261,7 +272,7 @@ dependencies = [
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "0.1.3" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -284,7 +295,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "vec_map" name = "vec_map"
version = "0.6.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"
[[package]] [[package]]
@@ -298,7 +309,7 @@ version = "1.0.7"
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)",
"same-file 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "same-file 0.1.3 (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)",
] ]
@@ -314,43 +325,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "wincolor" name = "wincolor"
version = "0.1.2" version = "0.1.3"
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)",
] ]
[metadata] [metadata]
"checksum aho-corasick 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f660b942762979b56c9f07b4b36bb559776fbad102f05d6771e1b629e8fd5bf" "checksum aho-corasick 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0638fd549427caa90c499814196d1b9e3725eb4d15d7339d6de073a680ed0ca2"
"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.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e1ab483fc81a8143faa7203c4a3c02888ebd1a782e37e41fa34753ba9a162"
"checksum bytecount 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1e8f09fbc8c6726a4b616dcfbd4f54491068d6bb1b93ac03c78ac18ff9a5924a" "checksum bytecount 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1e8f09fbc8c6726a4b616dcfbd4f54491068d6bb1b93ac03c78ac18ff9a5924a"
"checksum clap 2.19.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95b78f3fe0fc94c13c731714363260e04b557a637166f33a4570d3189d642374" "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
"checksum clap 2.21.1 (registry+https://github.com/rust-lang/crates.io-index)" = "74a80f603221c9cd9aa27a28f52af452850051598537bb6b359c38a7d61e5cda"
"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 env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum encoding_rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a1cca0a26f904955d80d70b9bff1019e4f4cbc06f2fcbccf8bd3d889cc1c9b7"
"checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83"
"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.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "640001e1bd865c7c32806292822445af576a6866175b5225aa2087ca5e3de551" "checksum fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34edaee07555859dc13ca387e6ae05686bb4d0364c95d649b6dab959511f4baf"
"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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" "checksum lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7291b1dd97d331f752620b02dfdbc231df7fc01bf282a00769e1cdb963c460dc"
"checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5" "checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135"
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" "checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad"
"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.0 (registry+https://github.com/rust-lang/crates.io-index)" = "065ce59af31c18ea2c419100bda6247dd4ec3099423202b12f0bd32e529fabd2" "checksum memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46f3c7359028b31999287dae4e5047ddfe90a23b7dca2282ce759b491080c99b"
"checksum num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a225d1e2717567599c24f88e49f00856c6e825a12125181ee42c4257e3688d39" "checksum num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a18c392466409c50b87369414a2680c93e739aedeb498eb2bff7d7eb569744e2"
"checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01" "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01"
"checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457"
"checksum same-file 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c722bde68d432ad7982a6431b13264cc558af1707c0f321820e238c5671856ea" "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 strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f84c44fbb2f91db7fef94554e6b2ac05909c9c0b0bc23bb98d3a1aebfe7f7c" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
"checksum term_size 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f7f5f3f71b0040cecc71af239414c23fd3c73570f5ff54cf50e03cef637f2a0" "checksum term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "07b6c1ac5b3fffd75073276bca1ceed01f67a28537097a2a9539e116e50fb21a"
"checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" "checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a"
"checksum thread_local 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7793b722f0f77ce716e7f1acf416359ca32ff24d04ffbac4269f44a4a83be05d" "checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7"
"checksum unicode-segmentation 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c3bc443ded17b11305ffffe6b37e2076f328a5a8cb6aa877b1b98f77699e98b5" "checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3"
"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 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"
"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.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac5efe5cb0fa14ec2f84f83c701c562ee63f6dcc680861b21d65c682adfb05f" "checksum vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8cdc8b93bd0198ed872357fb2e667f7125646b1762f16d60b2c96350d361897"
"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.4.0" #:version version = "0.5.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
@@ -28,8 +28,9 @@ path = "tests/tests.rs"
[dependencies] [dependencies]
atty = "0.2.2" atty = "0.2.2"
bytecount = "0.1.4" bytecount = "0.1.4"
clap = "2.19.0" clap = "2.20.5"
env_logger = { version = "0.3", default-features = false } encoding_rs = "0.5.0"
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.7", path = "ignore" } ignore = { version = "0.1.7", path = "ignore" }
lazy_static = "0.2" lazy_static = "0.2"
@@ -40,7 +41,7 @@ memmap = "0.5"
num_cpus = "1" num_cpus = "1"
regex = "0.2.1" regex = "0.2.1"
same-file = "0.1.1" same-file = "0.1.1"
termcolor = { version = "0.2.0", path = "termcolor" } termcolor = { version = "0.3.0", path = "termcolor" }
[build-dependencies] [build-dependencies]
clap = "2.18" clap = "2.18"

View File

@@ -1,7 +1,7 @@
ripgrep (rg) ripgrep (rg)
------------ ------------
`ripgrep` is a line oriented search tool that combines the usability of The `ripgrep` is a line oriented search tool that combines the usability of The
Silver Searcher (an `ack` clone) with the raw speed of GNU grep. `ripgrep` Silver Searcher (similar to `ack`) with the raw speed of GNU grep. `ripgrep`
works by recursively searching your current directory for a regex pattern. works by recursively searching your current directory for a regex pattern.
`ripgrep` has first class support on Windows, Mac and Linux, with binary `ripgrep` has first class support on Windows, Mac and Linux, with binary
downloads available for downloads available for
@@ -13,6 +13,10 @@ downloads available for
Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org). Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org).
### CHANGELOG
Please see the [CHANGELOG](CHANGELOG.md) for a release history.
### Screenshot of search results ### Screenshot of search results
[![A screenshot of a sample search with ripgrep](http://burntsushi.net/stuff/ripgrep1.png)](http://burntsushi.net/stuff/ripgrep1.png) [![A screenshot of a sample search with ripgrep](http://burntsushi.net/stuff/ripgrep1.png)](http://burntsushi.net/stuff/ripgrep1.png)
@@ -83,6 +87,10 @@ increases the times to `3.081s` for ripgrep and `11.403s` for GNU grep.
of search results, searching multiple patterns, highlighting matches with of search results, searching multiple patterns, highlighting matches with
color and full Unicode support. Unlike GNU grep, `ripgrep` stays fast while color and full Unicode support. Unlike GNU grep, `ripgrep` stays fast while
supporting Unicode (which is always on). supporting Unicode (which is always on).
* `ripgrep` supports searching files in text encodings other than UTF-8, such
as UTF-16, latin-1, GBK, EUC-JP, Shift_JIS and more. (Some support for
automatically detecting UTF-16 is provided. Other text encodings must be
specifically specified with the `-E/--encoding` flag.)
In other words, use `ripgrep` if you like speed, filtering by default, fewer In other words, use `ripgrep` if you like speed, filtering by default, fewer
bugs and Unicode support. bugs and Unicode support.
@@ -101,18 +109,12 @@ give you a glimpse at some important downsides or missing features of
support for Unicode categories (e.g., `\p{Sc}` to match currency symbols or support for Unicode categories (e.g., `\p{Sc}` to match currency symbols or
`\p{Lu}` to match any uppercase letter). (Fancier regexes will never be `\p{Lu}` to match any uppercase letter). (Fancier regexes will never be
supported.) supported.)
* If you need to search files with text encodings other than UTF-8 (like
UTF-16), then `ripgrep` won't work. `ripgrep` will still work on ASCII
compatible encodings like latin1 or otherwise partially valid UTF-8.
`ripgrep` *can* search for arbitrary bytes though, which might work in
a pinch. (Likely to be supported in the future.)
* `ripgrep` doesn't yet support searching compressed files. (Likely to be * `ripgrep` doesn't yet support searching compressed files. (Likely to be
supported in the future.) supported in the future.)
* `ripgrep` doesn't have multiline search. (Unlikely to ever be supported.) * `ripgrep` doesn't have multiline search. (Unlikely to ever be supported.)
In other words, if you like fancy regexes, non-UTF-8 character encodings, In other words, if you like fancy regexes, searching compressed files or
searching compressed files or multiline search, then `ripgrep` may not quite multiline search, then `ripgrep` may not quite meet your needs (yet).
meet your needs (yet).
### Is it really faster than everything else? ### Is it really faster than everything else?
@@ -167,6 +169,12 @@ $ brew tap burntsushi/ripgrep https://github.com/BurntSushi/ripgrep.git
$ brew install burntsushi/ripgrep/ripgrep-bin $ brew install burntsushi/ripgrep/ripgrep-bin
``` ```
If you're a **Windows Chocolatey** user, then you can install `ripgrep` from the [official repo](https://chocolatey.org/packages/ripgrep):
```
$ choco install ripgrep
```
If you're an **Arch Linux** user, then you can install `ripgrep` from the official repos: If you're an **Arch Linux** user, then you can install `ripgrep` from the official repos:
``` ```
@@ -370,3 +378,17 @@ $ cargo test
``` ```
from the repository root. from the repository root.
### Known issues
#### 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
original foreground color.
PR [#187](https://github.com/BurntSushi/ripgrep/pull/187) fixed this, and it
was later deprecated in
[#281](https://github.com/BurntSushi/ripgrep/issues/281). A full explanation is
available [here][msys issue explanation].
[msys issue explanation]: https://github.com/BurntSushi/ripgrep/issues/281#issuecomment-269093893

View File

@@ -1,4 +1,4 @@
.\" Automatically generated by Pandoc 1.19.1 .\" Automatically generated by Pandoc 1.19.2.1
.\" .\"
.TH "rg" "1" .TH "rg" "1"
.hy .hy
@@ -64,20 +64,29 @@ Treat the pattern as a literal string instead of a regular expression.
.TP .TP
.B \-g, \-\-glob \f[I]GLOB\f[] ... .B \-g, \-\-glob \f[I]GLOB\f[] ...
Include or exclude files for searching that match the given glob. Include or exclude files for searching that match the given glob.
This always overrides any other ignore logic. 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. 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
.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).
.PP
For example: rg \-g \[aq]<glob>\[aq] \-\-files
.RE .RE
.PP
The \-\-glob flag subsumes the functionality of both the \-\-include and
\-\-exclude flags commonly found in other tools.
.IP
.nf
\f[C]
Values\ given\ to\ \-g\ must\ be\ quoted\ or\ your\ shell\ will\ expand\ them\ and\ result
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[]
.fi
.TP .TP
.B \-h, \-\-help .B \-h, \-\-help
Show this usage message. Show this usage message.
@@ -127,6 +136,12 @@ 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 grep \-r, and \-uuu is equivalent to grep \-a \-r.
.RS .RS
.PP
Note that the \-u flags are convenient aliases for other combinations of
flags.
\-u aliases \[aq]\-\-no\-ignore\[aq].
\-uu aliases \[aq]\-\-no\-ignore \-\-hidden\[aq].
\-uuu aliases \[aq]\-\-no\-ignore \-\-hidden \-\-text\[aq].
.RE .RE
.TP .TP
.B \-v, \-\-invert\-match .B \-v, \-\-invert\-match
@@ -199,6 +214,15 @@ Show debug messages.
.RS .RS
.RE .RE
.TP .TP
.B \-E, \-\-encoding \f[I]ENCODING\f[]
Specify the text encoding that ripgrep will use on all files searched.
The default value is \[aq]auto\[aq], which will cause ripgrep to do a
best effort automatic detection of encoding on a per\-file basis.
Other supported values can be found in the list of labels here:
https://encoding.spec.whatwg.org/#concept\-encoding\-get
.RS
.RE
.TP
.B \-f, \-\-file FILE ... .B \-f, \-\-file FILE ...
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
@@ -275,11 +299,21 @@ Follow symlinks.
.RS .RS
.RE .RE
.TP .TP
.B \-m, \-\-max\-count NUM .B \-m, \-\-max\-count \f[I]NUM\f[]
Limit the number of matching lines per file searched to NUM. Limit the number of matching lines per file searched to NUM.
.RS .RS
.RE .RE
.TP .TP
.B \-\-max\-filesize \f[I]NUM\f[]+\f[I]SUFFIX\f[]?
Ignore files larger than \f[I]NUM\f[] in size.
Directories will never be ignored.
.RS
.PP
\f[I]SUFFIX\f[] is optional and may be one of K, M or G.
These correspond to kilobytes, megabytes and gigabytes respectively.
If omitted the input is treated as bytes.
.RE
.TP
.B \-\-maxdepth \f[I]NUM\f[] .B \-\-maxdepth \f[I]NUM\f[]
Descend at most NUM directories below the command line arguments. Descend at most NUM directories below the command line arguments.
A value of zero searches only the starting\-points themselves. A value of zero searches only the starting\-points themselves.

View File

@@ -49,8 +49,13 @@ Project home page: https://github.com/BurntSushi/ripgrep
-g, --glob *GLOB* ... -g, --glob *GLOB* ...
: Include or exclude files for searching that match the given glob. This always : Include or exclude files for searching that match the given glob. This always
overrides any other ignore logic. Multiple glob flags may be used. Globbing overrides any other ignore logic if there is a conflict, but is otherwise
rules match .gitignore globs. Precede a glob with a '!' to exclude it. 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.
The --glob flag subsumes the functionality of both the --include and
--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.
@@ -89,6 +94,10 @@ Project home page: https://github.com/BurntSushi/ripgrep
-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 is
equivalent to grep -a -r. equivalent to grep -a -r.
Note that the -u flags are convenient aliases for other combinations of
flags. -u aliases '--no-ignore'. -uu aliases '--no-ignore --hidden'.
-uuu aliases '--no-ignore --hidden --text'.
-v, --invert-match -v, --invert-match
: Invert matching. : Invert matching.
@@ -136,6 +145,13 @@ Project home page: https://github.com/BurntSushi/ripgrep
--debug --debug
: Show debug messages. : Show debug messages.
-E, --encoding *ENCODING*
: Specify the text encoding that ripgrep will use on all files
searched. The default value is 'auto', which will cause ripgrep to do
a best effort automatic detection of encoding on a per-file basis.
Other supported values can be found in the list of labels here:
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,
@@ -187,9 +203,20 @@ Project home page: https://github.com/BurntSushi/ripgrep
-L, --follow -L, --follow
: Follow symlinks. : Follow symlinks.
-m, --max-count NUM -M, --max-columns *NUM*
: Don't print lines longer than this limit in bytes. Longer lines are omitted,
and only the number of matches in that line is printed.
-m, --max-count *NUM*
: Limit the number of matching lines per file searched to NUM. : Limit the number of matching lines per file searched to NUM.
--max-filesize *NUM*+*SUFFIX*?
: Ignore files larger than *NUM* in size. Directories will never be ignored.
*SUFFIX* is optional and may be one of K, M or G. These correspond to
kilobytes, megabytes and gigabytes respectively. If omitted the input is
treated as bytes.
--maxdepth *NUM* --maxdepth *NUM*
: Descend at most NUM directories below the command line arguments. : Descend at most NUM directories below the command line arguments.
A value of zero searches only the starting-points themselves. A value of zero searches only the starting-points themselves.

3
globset/COPYING Normal file
View File

@@ -0,0 +1,3 @@
This project is dual-licensed under the Unlicense and MIT licenses.
You may use this code under the terms of either license.

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "globset" name = "globset"
version = "0.1.3" #:version version = "0.1.4" #: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

21
globset/LICENSE-MIT Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Andrew Gallant
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

24
globset/UNLICENSE Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

View File

@@ -659,9 +659,18 @@ impl Tokens {
for pat in patterns { for pat in patterns {
let mut altre = String::new(); let mut altre = String::new();
self.tokens_to_regex(options, &pat, &mut altre); self.tokens_to_regex(options, &pat, &mut altre);
parts.push(altre); if !altre.is_empty() {
parts.push(altre);
}
}
// It is possible to have an empty set in which case the
// resulting alternation '()' would be an error.
if !parts.is_empty() {
re.push('(');
re.push_str(&parts.join("|"));
re.push(')');
} }
re.push_str(&parts.join("|"));
} }
} }
} }

3
grep/COPYING Normal file
View File

@@ -0,0 +1,3 @@
This project is dual-licensed under the Unlicense and MIT licenses.
You may use this code under the terms of either license.

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "grep" name = "grep"
version = "0.1.5" #:version version = "0.1.6" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"] authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """ description = """
Fast line oriented regex searching as a library. Fast line oriented regex searching as a library.

21
grep/LICENSE-MIT Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Andrew Gallant
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

24
grep/UNLICENSE Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

3
ignore/COPYING Normal file
View File

@@ -0,0 +1,3 @@
This project is dual-licensed under the Unlicense and MIT licenses.
You may use this code under the terms of either license.

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "ignore" name = "ignore"
version = "0.1.7" #:version version = "0.1.8" #: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.3", path = "../globset" } globset = { version = "0.1.4", path = "../globset" }
lazy_static = "0.2" lazy_static = "0.2"
log = "0.3" log = "0.3"
memchr = "1" memchr = "1"

21
ignore/LICENSE-MIT Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Andrew Gallant
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

24
ignore/UNLICENSE Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

View File

@@ -368,9 +368,10 @@ impl GitignoreBuilder {
}; };
let mut literal_separator = false; let mut literal_separator = false;
let has_slash = line.chars().any(|c| c == '/'); let has_slash = line.chars().any(|c| c == '/');
let is_absolute = line.chars().nth(0).unwrap() == '/'; let mut is_absolute = false;
if line.starts_with("\\!") || line.starts_with("\\#") { if line.starts_with("\\!") || line.starts_with("\\#") {
line = &line[1..]; line = &line[1..];
is_absolute = line.chars().nth(0) == Some('/');
} else { } else {
if line.starts_with("!") { if line.starts_with("!") {
glob.is_whitelist = true; glob.is_whitelist = true;
@@ -383,6 +384,7 @@ impl GitignoreBuilder {
// simply banning wildcards from matching /. // simply banning wildcards from matching /.
literal_separator = true; literal_separator = true;
line = &line[1..]; line = &line[1..];
is_absolute = true;
} }
} }
// If it ends with a slash, then this should only match directories, // If it ends with a slash, then this should only match directories,
@@ -570,6 +572,7 @@ mod tests {
not_ignored!( not_ignored!(
ignot14, "./third_party/protobuf", "m4/ltoptions.m4", ignot14, "./third_party/protobuf", "m4/ltoptions.m4",
"./third_party/protobuf/csharp/src/packages/repositories.config"); "./third_party/protobuf/csharp/src/packages/repositories.config");
not_ignored!(ignot15, ROOT, "!/bar", "foo/bar");
fn bytes(s: &str) -> Vec<u8> { fn bytes(s: &str) -> Vec<u8> {
s.to_string().into_bytes() s.to_string().into_bytes()

View File

@@ -214,4 +214,10 @@ mod tests {
assert!(ov.matched("src/foo", false).is_ignore()); assert!(ov.matched("src/foo", false).is_ignore());
assert!(ov.matched("src/foo", true).is_none()); assert!(ov.matched("src/foo", true).is_none());
} }
#[test]
fn absolute_path() {
let ov = ov(&["!/bar"]);
assert!(ov.matched("./foo/bar", false).is_none());
}
} }

View File

@@ -116,7 +116,7 @@ const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
("crystal", &["Projectfile", "*.cr"]), ("crystal", &["Projectfile", "*.cr"]),
("cs", &["*.cs"]), ("cs", &["*.cs"]),
("csharp", &["*.cs"]), ("csharp", &["*.cs"]),
("css", &["*.css"]), ("css", &["*.css", "*.scss"]),
("cython", &["*.pyx"]), ("cython", &["*.pyx"]),
("dart", &["*.dart"]), ("dart", &["*.dart"]),
("d", &["*.d"]), ("d", &["*.d"]),
@@ -134,7 +134,7 @@ const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
("h", &["*.h", "*.hpp"]), ("h", &["*.h", "*.hpp"]),
("hbs", &["*.hbs"]), ("hbs", &["*.hbs"]),
("haskell", &["*.hs", "*.lhs"]), ("haskell", &["*.hs", "*.lhs"]),
("html", &["*.htm", "*.html"]), ("html", &["*.htm", "*.html", "*.ejs"]),
("java", &["*.java"]), ("java", &["*.java"]),
("jinja", &["*.jinja", "*.jinja2"]), ("jinja", &["*.jinja", "*.jinja2"]),
("js", &[ ("js", &[
@@ -145,6 +145,7 @@ const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
("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"]),
("lua", &["*.lua"]), ("lua", &["*.lua"]),
("m4", &["*.ac", "*.m4"]), ("m4", &["*.ac", "*.m4"]),
("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk", "*.mak"]), ("make", &["gnumakefile", "Gnumakefile", "makefile", "Makefile", "*.mk", "*.mak"]),
@@ -177,6 +178,7 @@ const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
("stylus", &["*.styl"]), ("stylus", &["*.styl"]),
("sql", &["*.sql"]), ("sql", &["*.sql"]),
("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]), ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
("svg", &["*.svg"]),
("swift", &["*.swift"]), ("swift", &["*.swift"]),
("swig", &["*.def", "*.i"]), ("swig", &["*.def", "*.i"]),
("taskpaper", &["*.taskpaper"]), ("taskpaper", &["*.taskpaper"]),

View File

@@ -392,7 +392,9 @@ impl DirEntryRaw {
/// continues. /// 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, if the path has made it this far then it is yielded in the /// * Sixth, unless the path is a directory, the size of the file is compared
/// against the max filesize limit. If it exceeds the limit, it is skipped.
/// * Seventh, if the path has made it this far then it is yielded in the
/// iterator. /// iterator.
#[derive(Clone)] #[derive(Clone)]
pub struct WalkBuilder { pub struct WalkBuilder {
@@ -400,6 +402,7 @@ pub struct WalkBuilder {
ig_builder: IgnoreBuilder, ig_builder: IgnoreBuilder,
parents: bool, parents: bool,
max_depth: Option<usize>, max_depth: Option<usize>,
max_filesize: Option<u64>,
follow_links: bool, follow_links: bool,
sorter: Option<Arc<Fn(&OsString, &OsString) -> cmp::Ordering + 'static>>, sorter: Option<Arc<Fn(&OsString, &OsString) -> cmp::Ordering + 'static>>,
threads: usize, threads: usize,
@@ -412,6 +415,7 @@ impl fmt::Debug for WalkBuilder {
.field("ig_builder", &self.ig_builder) .field("ig_builder", &self.ig_builder)
.field("parents", &self.parents) .field("parents", &self.parents)
.field("max_depth", &self.max_depth) .field("max_depth", &self.max_depth)
.field("max_filesize", &self.max_filesize)
.field("follow_links", &self.follow_links) .field("follow_links", &self.follow_links)
.field("threads", &self.threads) .field("threads", &self.threads)
.finish() .finish()
@@ -431,6 +435,7 @@ impl WalkBuilder {
ig_builder: IgnoreBuilder::new(), ig_builder: IgnoreBuilder::new(),
parents: true, parents: true,
max_depth: None, max_depth: None,
max_filesize: None,
follow_links: false, follow_links: false,
sorter: None, sorter: None,
threads: 0, threads: 0,
@@ -464,6 +469,7 @@ impl WalkBuilder {
it: None, it: None,
ig_root: ig_root.clone(), ig_root: ig_root.clone(),
ig: ig_root.clone(), ig: ig_root.clone(),
max_filesize: self.max_filesize,
parents: self.parents, parents: self.parents,
} }
} }
@@ -478,6 +484,7 @@ impl WalkBuilder {
paths: self.paths.clone().into_iter(), paths: self.paths.clone().into_iter(),
ig_root: self.ig_builder.build(), ig_root: self.ig_builder.build(),
max_depth: self.max_depth, max_depth: self.max_depth,
max_filesize: self.max_filesize,
follow_links: self.follow_links, follow_links: self.follow_links,
parents: self.parents, parents: self.parents,
threads: self.threads, threads: self.threads,
@@ -508,6 +515,12 @@ impl WalkBuilder {
self self
} }
/// Whether to ignore files above the specified limit.
pub fn max_filesize(&mut self, filesize: Option<u64>) -> &mut WalkBuilder {
self.max_filesize = filesize;
self
}
/// The number of threads to use for traversal. /// The number of threads to use for traversal.
/// ///
/// Note that this only has an effect when using `build_parallel`. /// Note that this only has an effect when using `build_parallel`.
@@ -650,6 +663,7 @@ pub struct Walk {
it: Option<WalkEventIter>, it: Option<WalkEventIter>,
ig_root: Ignore, ig_root: Ignore,
ig: Ignore, ig: Ignore,
max_filesize: Option<u64>,
parents: bool, parents: bool,
} }
@@ -667,7 +681,17 @@ impl Walk {
if ent.depth() == 0 { if ent.depth() == 0 {
return false; return false;
} }
skip_path(&self.ig, ent.path(), ent.file_type().is_dir())
let is_dir = ent.file_type().is_dir();
let max_size = self.max_filesize;
let should_skip_path = skip_path(&self.ig, ent.path(), is_dir);
let should_skip_filesize = if !is_dir && max_size.is_some() {
skip_filesize(max_size.unwrap(), ent.path(), &ent.metadata().ok())
} else {
false
};
should_skip_path || should_skip_filesize
} }
} }
@@ -824,6 +848,7 @@ pub struct WalkParallel {
paths: vec::IntoIter<PathBuf>, paths: vec::IntoIter<PathBuf>,
ig_root: Ignore, ig_root: Ignore,
parents: bool, parents: bool,
max_filesize: Option<u64>,
max_depth: Option<usize>, max_depth: Option<usize>,
follow_links: bool, follow_links: bool,
threads: usize, threads: usize,
@@ -886,6 +911,7 @@ impl WalkParallel {
threads: threads, threads: threads,
parents: self.parents, parents: self.parents,
max_depth: self.max_depth, max_depth: self.max_depth,
max_filesize: self.max_filesize,
follow_links: self.follow_links, follow_links: self.follow_links,
}; };
handles.push(thread::spawn(|| worker.run())); handles.push(thread::spawn(|| worker.run()));
@@ -1000,6 +1026,9 @@ struct Worker {
/// The maximum depth of directories to descend. A value of `0` means no /// The maximum depth of directories to descend. A value of `0` means no
/// descension at all. /// descension at all.
max_depth: Option<usize>, max_depth: Option<usize>,
/// The maximum size a searched file can be (in bytes). If a file exceeds
/// this size it will be skipped.
max_filesize: Option<u64>,
/// Whether to follow symbolic links or not. When this is enabled, loop /// Whether to follow symbolic links or not. When this is enabled, loop
/// detection is performed. /// detection is performed.
follow_links: bool, follow_links: bool,
@@ -1106,7 +1135,15 @@ impl Worker {
} }
} }
let is_dir = dent.file_type().map_or(false, |ft| ft.is_dir()); let is_dir = dent.file_type().map_or(false, |ft| ft.is_dir());
if !skip_path(ig, dent.path(), is_dir) { let max_size = self.max_filesize;
let should_skip_path = skip_path(ig, dent.path(), is_dir);
let should_skip_filesize = if !is_dir && max_size.is_some() {
skip_filesize(max_size.unwrap(), dent.path(), &dent.metadata().ok())
} else {
false
};
if !should_skip_path && !should_skip_filesize {
self.queue.push(Message::Work(Work { self.queue.push(Message::Work(Work {
dent: dent, dent: dent,
ignore: ig.clone(), ignore: ig.clone(),
@@ -1253,6 +1290,30 @@ fn check_symlink_loop(
Ok(()) Ok(())
} }
// Before calling this function, make sure that you ensure that is really
// necessary as the arguments imply a file stat.
fn skip_filesize(
max_filesize: u64,
path: &Path,
ent: &Option<Metadata>
) -> bool {
let filesize = match *ent {
Some(ref md) => Some(md.len()),
None => None
};
if let Some(fs) = filesize {
if fs > max_filesize {
debug!("ignoring {}: {} bytes", path.display(), fs);
true
} else {
false
}
} else {
false
}
}
fn skip_path(ig: &Ignore, path: &Path, is_dir: bool) -> bool { fn skip_path(ig: &Ignore, path: &Path, is_dir: bool) -> bool {
let m = ig.matched(path, is_dir); let m = ig.matched(path, is_dir);
if m.is_ignore() { if m.is_ignore() {
@@ -1282,6 +1343,11 @@ mod tests {
file.write_all(contents.as_bytes()).unwrap(); file.write_all(contents.as_bytes()).unwrap();
} }
fn wfile_size<P: AsRef<Path>>(path: P, size: u64) {
let file = File::create(path).unwrap();
file.set_len(size).unwrap();
}
#[cfg(unix)] #[cfg(unix)]
fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) { fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) {
use std::os::unix::fs::symlink; use std::os::unix::fs::symlink;
@@ -1438,6 +1504,32 @@ mod tests {
]); ]);
} }
#[test]
fn max_filesize() {
let td = TempDir::new("walk-test-").unwrap();
mkdirp(td.path().join("a/b"));
wfile_size(td.path().join("foo"), 0);
wfile_size(td.path().join("bar"), 400);
wfile_size(td.path().join("baz"), 600);
wfile_size(td.path().join("a/foo"), 600);
wfile_size(td.path().join("a/bar"), 500);
wfile_size(td.path().join("a/baz"), 200);
let mut builder = WalkBuilder::new(td.path());
assert_paths(td.path(), &builder, &[
"a", "a/b", "foo", "bar", "baz", "a/foo", "a/bar", "a/baz",
]);
assert_paths(td.path(), builder.max_filesize(Some(0)), &[
"a", "a/b", "foo"
]);
assert_paths(td.path(), builder.max_filesize(Some(500)), &[
"a", "a/b", "foo", "bar", "a/bar", "a/baz"
]);
assert_paths(td.path(), builder.max_filesize(Some(50000)), &[
"a", "a/b", "foo", "bar", "baz", "a/foo", "a/bar", "a/baz",
]);
}
#[cfg(unix)] // because symlinks on windows are weird #[cfg(unix)] // because symlinks on windows are weird
#[test] #[test]
fn symlinks() { fn symlinks() {

View File

@@ -1,16 +1,16 @@
class RipgrepBin < Formula class RipgrepBin < Formula
version '0.4.0' version '0.5.0'
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 "6ac71251909227f8ef7eda27d3080c954843f3665b81e455362c90b2a9c4734a" sha256 "efdc64456fc64910c2998b25c402aac2ff17caf1507e8bcca4584b429ef7f7ae"
conflicts_with "ripgrep" conflicts_with "ripgrep"
def install def install
bin.install "rg" bin.install "rg"
man1.install "rg.1" man1.install "rg.1"
bash_completion.install "complete/rg.bash-completion" bash_completion.install "complete/rg.bash-completion"
fish_completion.install "complete/rg.fish" fish_completion.install "complete/rg.fish"
zsh_completion.install "complete/_rg" zsh_completion.install "complete/_rg"

View File

@@ -68,12 +68,12 @@ fn app<F>(next_line_help: bool, doc: F) -> App<'static, 'static>
// consistent with short/long views. // consistent with short/long views.
.arg(arg("help-short").short("h")) .arg(arg("help-short").short("h"))
.arg(flag("help")) .arg(flag("help"))
.arg(flag("version").short("V")) .arg(arg("ripgrep-version").long("version").short("V"))
// 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",
"version", "ripgrep-version",
])) ]))
.arg(arg("path").multiple(true)) .arg(arg("path").multiple(true))
.arg(flag("regexp").short("e") .arg(flag("regexp").short("e")
@@ -96,6 +96,8 @@ fn app<F>(next_line_help: bool, doc: F) -> App<'static, 'static>
.possible_values(&["never", "auto", "always", "ansi"])) .possible_values(&["never", "auto", "always", "ansi"]))
.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")
.takes_value(true).number_of_values(1))
.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)
@@ -135,8 +137,8 @@ fn app<F>(next_line_help: bool, doc: F) -> App<'static, 'static>
.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"))
.arg(flag("heading")) .arg(flag("heading").overrides_with("no-heading"))
.arg(flag("no-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)
@@ -145,6 +147,8 @@ fn app<F>(next_line_help: bool, doc: F) -> App<'static, 'static>
.arg(flag("max-count") .arg(flag("max-count")
.short("m").value_name("NUM").takes_value(true) .short("m").value_name("NUM").takes_value(true)
.validator(validate_number)) .validator(validate_number))
.arg(flag("max-filesize")
.value_name("NUM+SUFFIX?").takes_value(true))
.arg(flag("maxdepth") .arg(flag("maxdepth")
.value_name("NUM").takes_value(true) .value_name("NUM").takes_value(true)
.validator(validate_number)) .validator(validate_number))
@@ -165,6 +169,9 @@ fn app<F>(next_line_help: bool, doc: F) -> App<'static, 'static>
.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"))
.arg(flag("max-columns").short("M")
.value_name("NUM").takes_value(true)
.validator(validate_number))
.arg(flag("type-add") .arg(flag("type-add")
.value_name("TYPE").takes_value(true) .value_name("TYPE").takes_value(true)
.multiple(true).number_of_values(1)) .multiple(true).number_of_values(1))
@@ -199,7 +206,7 @@ lazy_static! {
doc!(h, "help", doc!(h, "help",
"Show verbose help output.", "Show verbose help output.",
"When given, more details about flags are provided."); "When given, more details about flags are provided.");
doc!(h, "version", doc!(h, "ripgrep-version",
"Prints version information."); "Prints version information.");
doc!(h, "pattern", doc!(h, "pattern",
@@ -249,6 +256,14 @@ lazy_static! {
change the match color to magenta and the background color for \ change the match color to magenta and the background color for \
line numbers to yellow:\n\n\ line numbers to yellow:\n\n\
rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo."); rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo.");
doc!(h, "encoding",
"Specify the text encoding of files to search.",
"Specify the text encoding that ripgrep will use on all files \
searched. The default value is 'auto', which will cause ripgrep \
to do a best effort automatic detection of encoding on a \
per-file basis. Other supported values can be found in the list \
of labels here: \
https://encoding.spec.whatwg.org/#concept-encoding-get");
doc!(h, "fixed-strings", doc!(h, "fixed-strings",
"Treat the pattern as a literal string.", "Treat the pattern as a literal string.",
"Treat the pattern as a literal string instead of a regular \ "Treat the pattern as a literal string instead of a regular \
@@ -333,9 +348,9 @@ lazy_static! {
provided are searched. Empty pattern lines will match all input \ provided are searched. Empty pattern lines will match all input \
lines, and the newline is not counted as part of the pattern."); lines, and the newline is not counted as part of the pattern.");
doc!(h, "files-with-matches", doc!(h, "files-with-matches",
"Only show the path of each file with at least one match."); "Only show the paths with at least one match.");
doc!(h, "files-without-match", doc!(h, "files-without-match",
"Only show the path of each file that contains zero matches."); "Only show the paths that contains zero matches.");
doc!(h, "with-filename", doc!(h, "with-filename",
"Show file name for each match.", "Show file name for each match.",
"Prefix each match with the file name that contains it. This is \ "Prefix each match with the file name that contains it. This is \
@@ -371,6 +386,13 @@ lazy_static! {
doc!(h, "max-count", doc!(h, "max-count",
"Limit the number of matches.", "Limit the number of matches.",
"Limit the number of matching lines per file searched to NUM."); "Limit the number of matching lines per file searched to NUM.");
doc!(h, "max-filesize",
"Ignore files larger than NUM in size.",
"Ignore files larger than NUM in size. Does not ignore directories. \
\n\nThe input format accepts suffixes of K, M or G which \
correspond to kilobytes, megabytes and gigabytes. If no suffix is \
provided the input is treated as bytes. \
\n\nExample: --max-filesize 50K or --max-filesize 80M");
doc!(h, "maxdepth", doc!(h, "maxdepth",
"Descend at most NUM directories.", "Descend at most NUM directories.",
"Limit the depth of directory traversal to NUM levels beyond \ "Limit the depth of directory traversal to NUM levels beyond \
@@ -454,6 +476,11 @@ lazy_static! {
"Show results with every match on its own line, including \ "Show results with every match on its own line, including \
line numbers and column numbers. With this option, a line with \ line numbers and column numbers. With this option, a line with \
more than one match will be printed more than once."); more than one match will be printed more than once.");
doc!(h, "max-columns",
"Don't print lines longer than this limit in bytes.",
"Don't print lines longer than this limit in bytes. Longer lines \
are omitted, and only the number of matches in that line is \
printed.");
doc!(h, "type-add", doc!(h, "type-add",
"Add a new glob for a file type.", "Add a new glob for a file type.",

View File

@@ -10,6 +10,7 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use clap; use clap;
use encoding_rs::Encoding;
use env_logger; use env_logger;
use grep::{Grep, GrepBuilder}; use grep::{Grep, GrepBuilder};
use log; use log;
@@ -41,6 +42,7 @@ pub struct Args {
column: bool, column: bool,
context_separator: Vec<u8>, context_separator: Vec<u8>,
count: bool, count: bool,
encoding: Option<&'static Encoding>,
files_with_matches: bool, files_with_matches: bool,
files_without_matches: bool, files_without_matches: bool,
eol: u8, eol: u8,
@@ -54,7 +56,9 @@ pub struct Args {
invert_match: bool, invert_match: bool,
line_number: bool, line_number: bool,
line_per_match: bool, line_per_match: bool,
max_columns: Option<usize>,
max_count: Option<u64>, max_count: Option<u64>,
max_filesize: Option<u64>,
maxdepth: Option<usize>, maxdepth: Option<usize>,
mmap: bool, mmap: bool,
no_ignore: bool, no_ignore: bool,
@@ -84,18 +88,23 @@ impl Args {
/// ///
/// Also, initialize a global logger. /// Also, initialize a global logger.
pub fn parse() -> Result<Args> { pub fn parse() -> Result<Args> {
let matches = app::app_short().get_matches(); use clap::ErrorKind::*;
let matches = match app::app_short().get_matches_safe() {
Ok(matches) => matches,
Err(clap::Error { kind: HelpDisplayed, .. }) => {
let _ = ::app::app_long().print_help();
println!("");
process::exit(0);
}
Err(err) => err.exit(),
};
if matches.is_present("help-short") { if matches.is_present("help-short") {
let _ = ::app::app_short().print_help(); let _ = ::app::app_short().print_help();
println!(""); println!("");
process::exit(0); process::exit(0);
} }
if matches.is_present("help") { if matches.is_present("ripgrep-version") {
let _ = ::app::app_long().print_help();
println!("");
process::exit(0);
}
if matches.is_present("version") {
println!("ripgrep {}", crate_version!()); println!("ripgrep {}", crate_version!());
process::exit(0); process::exit(0);
} }
@@ -153,7 +162,8 @@ impl Args {
.line_per_match(self.line_per_match) .line_per_match(self.line_per_match)
.null(self.null) .null(self.null)
.path_separator(self.path_separator) .path_separator(self.path_separator)
.with_filename(self.with_filename); .with_filename(self.with_filename)
.max_columns(self.max_columns);
if let Some(ref rep) = self.replace { if let Some(ref rep) = self.replace {
p = p.replace(rep.clone()); p = p.replace(rep.clone());
} }
@@ -223,6 +233,7 @@ impl Args {
.after_context(self.after_context) .after_context(self.after_context)
.before_context(self.before_context) .before_context(self.before_context)
.count(self.count) .count(self.count)
.encoding(self.encoding)
.files_with_matches(self.files_with_matches) .files_with_matches(self.files_with_matches)
.files_without_matches(self.files_without_matches) .files_without_matches(self.files_without_matches)
.eol(self.eol) .eol(self.eol)
@@ -285,6 +296,7 @@ impl Args {
wd.follow_links(self.follow); wd.follow_links(self.follow);
wd.hidden(!self.hidden); wd.hidden(!self.hidden);
wd.max_depth(self.maxdepth); wd.max_depth(self.maxdepth);
wd.max_filesize(self.max_filesize);
wd.overrides(self.glob_overrides.clone()); wd.overrides(self.glob_overrides.clone());
wd.types(self.types.clone()); wd.types(self.types.clone());
wd.git_global(!self.no_ignore && !self.no_ignore_vcs); wd.git_global(!self.no_ignore && !self.no_ignore_vcs);
@@ -314,6 +326,7 @@ impl<'a> ArgMatches<'a> {
/// configuration. /// configuration.
fn to_args(&self) -> Result<Args> { fn to_args(&self) -> Result<Args> {
let paths = self.paths(); let paths = self.paths();
let line_number = self.line_number(&paths);
let mmap = try!(self.mmap(&paths)); let mmap = try!(self.mmap(&paths));
let with_filename = self.with_filename(&paths); let with_filename = self.with_filename(&paths);
let (before_context, after_context) = try!(self.contexts()); let (before_context, after_context) = try!(self.contexts());
@@ -328,6 +341,7 @@ impl<'a> ArgMatches<'a> {
column: self.column(), column: self.column(),
context_separator: self.context_separator(), context_separator: self.context_separator(),
count: self.is_present("count"), count: self.is_present("count"),
encoding: try!(self.encoding()),
files_with_matches: self.is_present("files-with-matches"), files_with_matches: self.is_present("files-with-matches"),
files_without_matches: self.is_present("files-without-match"), files_without_matches: self.is_present("files-without-match"),
eol: b'\n', eol: b'\n',
@@ -339,9 +353,11 @@ impl<'a> ArgMatches<'a> {
hidden: self.hidden(), hidden: self.hidden(),
ignore_files: self.ignore_files(), ignore_files: self.ignore_files(),
invert_match: self.is_present("invert-match"), invert_match: self.is_present("invert-match"),
line_number: self.line_number(), line_number: line_number,
line_per_match: self.is_present("vimgrep"), line_per_match: self.is_present("vimgrep"),
max_columns: try!(self.usize_of("max-columns")),
max_count: try!(self.usize_of("max-count")).map(|max| max as u64), max_count: try!(self.usize_of("max-count")).map(|max| max as u64),
max_filesize: try!(self.max_filesize()),
maxdepth: try!(self.usize_of("maxdepth")), maxdepth: try!(self.usize_of("maxdepth")),
mmap: mmap, mmap: mmap,
no_ignore: self.no_ignore(), no_ignore: self.no_ignore(),
@@ -434,6 +450,9 @@ impl<'a> ArgMatches<'a> {
/// ///
/// 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>> {
if self.is_present("files") || self.is_present("type-list") {
return Ok(vec![self.empty_pattern()]);
}
let mut pats = vec![]; let mut pats = vec![];
match self.values_of_os("regexp") { match self.values_of_os("regexp") {
None => { None => {
@@ -563,6 +582,7 @@ impl<'a> ArgMatches<'a> {
/// will need to search. /// will need to search.
fn mmap(&self, paths: &[PathBuf]) -> Result<bool> { fn mmap(&self, paths: &[PathBuf]) -> Result<bool> {
let (before, after) = try!(self.contexts()); let (before, after) = try!(self.contexts());
let enc = try!(self.encoding());
Ok(if before > 0 || after > 0 || self.is_present("no-mmap") { Ok(if before > 0 || after > 0 || self.is_present("no-mmap") {
false false
} else if self.is_present("mmap") { } else if self.is_present("mmap") {
@@ -570,6 +590,10 @@ impl<'a> ArgMatches<'a> {
} else if cfg!(target_os = "macos") { } else if cfg!(target_os = "macos") {
// On Mac, memory maps appear to suck. Neat. // On Mac, memory maps appear to suck. Neat.
false false
} else if enc.is_some() {
// There's no practical way to transcode a memory map that isn't
// isomorphic to searching over io::Read.
false
} else { } else {
// If we're only searching a few paths and all of them are // If we're only searching a few paths and all of them are
// files, then memory maps are probably faster. // files, then memory maps are probably faster.
@@ -578,13 +602,14 @@ impl<'a> ArgMatches<'a> {
} }
/// Returns true if and only if line numbers should be shown. /// Returns true if and only if line numbers should be shown.
fn line_number(&self) -> bool { fn line_number(&self, paths: &[PathBuf]) -> bool {
if self.is_present("no-line-number") || self.is_present("count") { if self.is_present("no-line-number") || self.is_present("count") {
false false
} else { } else {
let only_stdin = paths == &[Path::new("-")];
self.is_present("line-number") self.is_present("line-number")
|| self.is_present("column") || self.is_present("column")
|| atty::is(atty::Stream::Stdout) || (atty::is(atty::Stream::Stdout) && !only_stdin)
|| self.is_present("pretty") || self.is_present("pretty")
|| self.is_present("vimgrep") || self.is_present("vimgrep")
} }
@@ -715,6 +740,29 @@ impl<'a> ArgMatches<'a> {
Ok(ColorSpecs::new(&specs)) Ok(ColorSpecs::new(&specs))
} }
/// Return the text encoding specified.
///
/// If the label given by the caller doesn't correspond to a valid
/// supported encoding (and isn't `auto`), then return an error.
///
/// A `None` encoding implies that the encoding should be automatically
/// detected on a per-file basis.
fn encoding(&self) -> Result<Option<&'static Encoding>> {
match self.0.value_of_lossy("encoding") {
None => Ok(None),
Some(label) => {
if label == "auto" {
return Ok(None);
}
match Encoding::for_label(label.as_bytes()) {
Some(enc) => Ok(Some(enc)),
None => Err(From::from(
format!("unsupported encoding: {}", label))),
}
}
}
}
/// Returns the approximate number of threads that ripgrep should use. /// Returns the approximate number of threads that ripgrep should use.
fn threads(&self) -> Result<usize> { fn threads(&self) -> Result<usize> {
if self.is_present("sort-files") { if self.is_present("sort-files") {
@@ -776,6 +824,31 @@ 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.
fn max_filesize(&self) -> Result<Option<u64>> {
use regex::Regex;
let max_filesize = match self.value_of_lossy("max-filesize") {
Some(x) => x,
None => return Ok(None)
};
let re = Regex::new("^([0-9]+)([KMG])?$").unwrap();
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());
match suffix {
None => Ok(Some(value)),
Some("K") => Ok(Some(value * 1024)),
Some("M") => Ok(Some(value * 1024 * 1024)),
Some("G") => Ok(Some(value * 1024 * 1024 * 1024)),
_ => Err(From::from("invalid suffix for max-filesize argument"))
}
}
/// 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")

461
src/decoder.rs Normal file
View File

@@ -0,0 +1,461 @@
#![allow(dead_code)]
use std::cmp;
use std::io::{self, Read};
use encoding_rs::{Decoder, Encoding, UTF_8};
/// A BOM is at least 2 bytes and at most 3 bytes.
///
/// If fewer than 2 bytes are available to be read at the beginning of a
/// reader, then a BOM is `None`.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Bom {
bytes: [u8; 3],
len: usize,
}
impl Bom {
fn as_slice(&self) -> &[u8] {
&self.bytes[0..self.len]
}
fn decoder(&self) -> Option<Decoder> {
let bom = self.as_slice();
if bom.len() < 3 {
return None;
}
if let Some((enc, _)) = Encoding::for_bom(bom) {
if enc != UTF_8 {
return Some(enc.new_decoder_with_bom_removal());
}
}
None
}
}
/// BomPeeker wraps `R` and satisfies the `io::Read` interface while also
/// providing a peek at the BOM if one exists. Peeking at the BOM does not
/// advance the reader.
struct BomPeeker<R> {
rdr: R,
bom: Option<Bom>,
nread: usize,
}
impl<R: io::Read> BomPeeker<R> {
/// Create a new BomPeeker.
///
/// The first three bytes can be read using the `peek_bom` method, but
/// will not advance the reader.
fn new(rdr: R) -> BomPeeker<R> {
BomPeeker { rdr: rdr, bom: None, nread: 0 }
}
/// Peek at the first three bytes of the underlying reader.
///
/// This does not advance the reader provided by `BomPeeker`.
///
/// If the underlying reader does not have at least two bytes available,
/// then `None` is returned.
fn peek_bom(&mut self) -> io::Result<Bom> {
if let Some(bom) = self.bom {
return Ok(bom);
}
self.bom = Some(Bom { bytes: [0; 3], len: 0 });
let mut buf = [0u8; 3];
let bom_len = try!(read_full(&mut self.rdr, &mut buf));
self.bom = Some(Bom { bytes: buf, len: bom_len });
Ok(self.bom.unwrap())
}
}
impl<R: io::Read> io::Read for BomPeeker<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.nread < 3 {
let bom = try!(self.peek_bom());
let bom = bom.as_slice();
if self.nread < bom.len() {
let rest = &bom[self.nread..];
let len = cmp::min(buf.len(), rest.len());
buf[..len].copy_from_slice(&rest[..len]);
self.nread += len;
return Ok(len);
}
}
let nread = try!(self.rdr.read(buf));
self.nread += nread;
Ok(nread)
}
}
/// Like io::Read::read_exact, except it never returns UnexpectedEof and
/// instead returns the number of bytes read if EOF is seen before filling
/// `buf`.
fn read_full<R: io::Read>(
mut rdr: R,
mut buf: &mut [u8],
) -> io::Result<usize> {
let mut nread = 0;
while !buf.is_empty() {
match rdr.read(buf) {
Ok(0) => break,
Ok(n) => {
nread += n;
let tmp = buf;
buf = &mut tmp[n..];
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(nread)
}
/// A reader that transcodes to UTF-8. The source encoding is determined by
/// inspecting the BOM from the stream read from `R`, if one exists. If a
/// UTF-16 BOM exists, then the source stream is trancoded to UTF-8 with
/// invalid UTF-16 sequences translated to the Unicode replacement character.
/// In all other cases, the underlying reader is passed through unchanged.
///
/// `R` is the type of the underlying reader and `B` is the type of an internal
/// buffer used to store the results of trancoding.
///
/// Note that not all methods on `io::Read` work with this implementation.
/// For example, the `bytes` adapter method attempts to read a single byte at
/// a time, but this implementation requires a buffer of size at least `4`. If
/// a buffer of size less than 4 is given, then an error is returned.
pub struct DecodeReader<R, B> {
/// The underlying reader, wrapped in a peeker for reading a BOM if one
/// exists.
rdr: BomPeeker<R>,
/// The internal buffer to store transcoded bytes before they are read by
/// callers.
buf: B,
/// The current position in `buf`. Subsequent reads start here.
pos: usize,
/// The number of transcoded bytes in `buf`. Subsequent reads end here.
buflen: usize,
/// Whether this is the first read or not (in which we inspect the BOM).
first: bool,
/// Whether a "last" read has occurred. After this point, EOF will always
/// be returned.
last: bool,
/// The underlying text decoder derived from the BOM, if one exists.
decoder: Option<Decoder>,
}
impl<R: io::Read, B: AsMut<[u8]>> DecodeReader<R, B> {
/// Create a new transcoder that converts a source stream to valid UTF-8.
///
/// If an encoding is specified, then it is used to transcode `rdr` to
/// UTF-8. Otherwise, if no encoding is specified, and if a UTF-16 BOM is
/// found, then the corresponding UTF-16 encoding is used to transcode
/// `rdr` to UTF-8. In all other cases, `rdr` is assumed to be at least
/// ASCII-compatible and passed through untouched.
///
/// Errors in the encoding of `rdr` are handled with the Unicode
/// replacement character. If no encoding of `rdr` is specified, then
/// errors are not handled.
pub fn new(
rdr: R,
buf: B,
enc: Option<&'static Encoding>,
) -> DecodeReader<R, B> {
DecodeReader {
rdr: BomPeeker::new(rdr),
buf: buf,
buflen: 0,
pos: 0,
first: enc.is_none(),
last: false,
decoder: enc.map(|enc| enc.new_decoder_with_bom_removal()),
}
}
/// Fill the internal buffer from the underlying reader.
///
/// If there are unread bytes in the internal buffer, then we move them
/// to the beginning of the internal buffer and fill the remainder.
///
/// If the internal buffer is too small to read additional bytes, then an
/// error is returned.
#[inline(always)] // massive perf benefit (???)
fn fill(&mut self) -> io::Result<()> {
if self.pos < self.buflen {
if self.buflen >= self.buf.as_mut().len() {
return Err(io::Error::new(
io::ErrorKind::Other,
"DecodeReader: internal buffer exhausted"));
}
let newlen = self.buflen - self.pos;
let mut tmp = Vec::with_capacity(newlen);
tmp.extend_from_slice(&self.buf.as_mut()[self.pos..self.buflen]);
self.buf.as_mut()[..newlen].copy_from_slice(&tmp);
self.buflen = newlen;
} else {
self.buflen = 0;
}
self.pos = 0;
self.buflen +=
try!(self.rdr.read(&mut self.buf.as_mut()[self.buflen..]));
Ok(())
}
/// Transcode the inner stream to UTF-8 in `buf`. This assumes that there
/// is a decoder capable of transcoding the inner stream to UTF-8. This
/// returns the number of bytes written to `buf`.
///
/// When this function returns, exactly one of the following things will
/// be true:
///
/// 1. A non-zero number of bytes were written to `buf`.
/// 2. The underlying reader reached EOF.
/// 3. An error is returned: the internal buffer ran out of room.
/// 4. An I/O error occurred.
///
/// Note that `buf` must have at least 4 bytes of space.
fn transcode(&mut self, buf: &mut [u8]) -> io::Result<usize> {
assert!(buf.len() >= 4);
if self.last {
return Ok(0);
}
if self.pos >= self.buflen {
try!(self.fill());
}
let mut nwrite = 0;
loop {
let (_, nin, nout, _) =
self.decoder.as_mut().unwrap().decode_to_utf8(
&self.buf.as_mut()[self.pos..self.buflen], buf, false);
self.pos += nin;
nwrite += nout;
// If we've written at least one byte to the caller-provided
// buffer, then our mission is complete.
if nwrite > 0 {
break;
}
// Otherwise, we know that our internal buffer has insufficient
// data to transcode at least one char, so we attempt to refill it.
try!(self.fill());
// Quit on EOF.
if self.buflen == 0 {
self.pos = 0;
self.last = true;
let (_, _, nout, _) =
self.decoder.as_mut().unwrap().decode_to_utf8(
&[], buf, true);
return Ok(nout);
}
}
Ok(nwrite)
}
#[inline(never)] // impacts perf...
fn detect(&mut self) -> io::Result<()> {
let bom = try!(self.rdr.peek_bom());
self.decoder = bom.decoder();
Ok(())
}
}
impl<R: io::Read, B: AsMut<[u8]>> io::Read for DecodeReader<R, B> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.first {
self.first = false;
try!(self.detect());
}
if self.decoder.is_none() {
return self.rdr.read(buf);
}
// When decoding UTF-8, we need at least 4 bytes of space to guarantee
// that we can decode at least one codepoint. If we don't have it, we
// can either return `0` for the number of bytes read or return an
// error. Since `0` would be interpreted as a possibly premature EOF,
// we opt for an error.
if buf.len() < 4 {
return Err(io::Error::new(
io::ErrorKind::Other,
"DecodeReader: byte buffer must have length at least 4"));
}
self.transcode(buf)
}
}
#[cfg(test)]
mod tests {
use std::io::Read;
use encoding_rs::Encoding;
use super::{Bom, BomPeeker, DecodeReader};
fn utf8(bytes: &[u8]) -> &str {
::std::str::from_utf8(bytes).unwrap()
}
fn read_to_string<R: Read>(mut rdr: R) -> String {
let mut s = String::new();
rdr.read_to_string(&mut s).unwrap();
s
}
#[test]
fn peeker_empty() {
let buf = [];
let mut peeker = BomPeeker::new(&buf[..]);
assert_eq!(Bom { bytes: [0; 3], len: 0}, peeker.peek_bom().unwrap());
let mut tmp = [0; 100];
assert_eq!(0, peeker.read(&mut tmp).unwrap());
}
#[test]
fn peeker_one() {
let buf = [1];
let mut peeker = BomPeeker::new(&buf[..]);
assert_eq!(
Bom { bytes: [1, 0, 0], len: 1},
peeker.peek_bom().unwrap());
let mut tmp = [0; 100];
assert_eq!(1, peeker.read(&mut tmp).unwrap());
assert_eq!(1, tmp[0]);
assert_eq!(0, peeker.read(&mut tmp).unwrap());
}
#[test]
fn peeker_two() {
let buf = [1, 2];
let mut peeker = BomPeeker::new(&buf[..]);
assert_eq!(
Bom { bytes: [1, 2, 0], len: 2},
peeker.peek_bom().unwrap());
let mut tmp = [0; 100];
assert_eq!(2, peeker.read(&mut tmp).unwrap());
assert_eq!(1, tmp[0]);
assert_eq!(2, tmp[1]);
assert_eq!(0, peeker.read(&mut tmp).unwrap());
}
#[test]
fn peeker_three() {
let buf = [1, 2, 3];
let mut peeker = BomPeeker::new(&buf[..]);
assert_eq!(
Bom { bytes: [1, 2, 3], len: 3},
peeker.peek_bom().unwrap());
let mut tmp = [0; 100];
assert_eq!(3, peeker.read(&mut tmp).unwrap());
assert_eq!(1, tmp[0]);
assert_eq!(2, tmp[1]);
assert_eq!(3, tmp[2]);
assert_eq!(0, peeker.read(&mut tmp).unwrap());
}
#[test]
fn peeker_four() {
let buf = [1, 2, 3, 4];
let mut peeker = BomPeeker::new(&buf[..]);
assert_eq!(
Bom { bytes: [1, 2, 3], len: 3},
peeker.peek_bom().unwrap());
let mut tmp = [0; 100];
assert_eq!(3, peeker.read(&mut tmp).unwrap());
assert_eq!(1, tmp[0]);
assert_eq!(2, tmp[1]);
assert_eq!(3, tmp[2]);
assert_eq!(1, peeker.read(&mut tmp).unwrap());
assert_eq!(4, tmp[0]);
assert_eq!(0, peeker.read(&mut tmp).unwrap());
}
#[test]
fn peeker_one_at_a_time() {
let buf = [1, 2, 3, 4];
let mut peeker = BomPeeker::new(&buf[..]);
let mut tmp = [0; 1];
assert_eq!(0, peeker.read(&mut tmp[..0]).unwrap());
assert_eq!(0, tmp[0]);
assert_eq!(1, peeker.read(&mut tmp).unwrap());
assert_eq!(1, tmp[0]);
assert_eq!(1, peeker.read(&mut tmp).unwrap());
assert_eq!(2, tmp[0]);
assert_eq!(1, peeker.read(&mut tmp).unwrap());
assert_eq!(3, tmp[0]);
assert_eq!(1, peeker.read(&mut tmp).unwrap());
assert_eq!(4, tmp[0]);
}
// In cases where all we have is a bom, we expect the bytes to be
// passed through unchanged.
#[test]
fn trans_utf16_bom() {
let srcbuf = vec![0xFF, 0xFE];
let mut dstbuf = vec![0; 8 * (1<<10)];
let mut rdr = DecodeReader::new(&*srcbuf, vec![0; 8 * (1<<10)], None);
let n = rdr.read(&mut dstbuf).unwrap();
assert_eq!(&*srcbuf, &dstbuf[..n]);
let srcbuf = vec![0xFE, 0xFF];
let mut rdr = DecodeReader::new(&*srcbuf, vec![0; 8 * (1<<10)], None);
let n = rdr.read(&mut dstbuf).unwrap();
assert_eq!(&*srcbuf, &dstbuf[..n]);
let srcbuf = vec![0xEF, 0xBB, 0xBF];
let mut rdr = DecodeReader::new(&*srcbuf, vec![0; 8 * (1<<10)], None);
let n = rdr.read(&mut dstbuf).unwrap();
assert_eq!(&*srcbuf, &dstbuf[..n]);
}
// Test basic UTF-16 decoding.
#[test]
fn trans_utf16_basic() {
let srcbuf = vec![0xFF, 0xFE, 0x61, 0x00];
let mut rdr = DecodeReader::new(&*srcbuf, vec![0; 8 * (1<<10)], None);
assert_eq!("a", read_to_string(&mut rdr));
let srcbuf = vec![0xFE, 0xFF, 0x00, 0x61];
let mut rdr = DecodeReader::new(&*srcbuf, vec![0; 8 * (1<<10)], None);
assert_eq!("a", read_to_string(&mut rdr));
}
// Test incomplete UTF-16 decoding. This ensures we see a replacement char
// if the stream ends with an unpaired code unit.
#[test]
fn trans_utf16_incomplete() {
let srcbuf = vec![0xFF, 0xFE, 0x61, 0x00, 0x00];
let mut rdr = DecodeReader::new(&*srcbuf, vec![0; 8 * (1<<10)], None);
assert_eq!("a\u{FFFD}", read_to_string(&mut rdr));
}
macro_rules! test_trans_simple {
($name:ident, $enc:expr, $srcbytes:expr, $dst:expr) => {
#[test]
fn $name() {
let srcbuf = &$srcbytes[..];
let enc = Encoding::for_label($enc.as_bytes());
let mut rdr = DecodeReader::new(
&*srcbuf, vec![0; 8 * (1<<10)], enc);
assert_eq!($dst, read_to_string(&mut rdr));
}
}
}
// This isn't exhaustive obviously, but it lets us test base level support.
test_trans_simple!(trans_simple_auto, "does not exist", b"\xD0\x96", "Ж");
test_trans_simple!(trans_simple_utf8, "utf-8", b"\xD0\x96", "Ж");
test_trans_simple!(trans_simple_utf16le, "utf-16le", b"\x16\x04", "Ж");
test_trans_simple!(trans_simple_utf16be, "utf-16be", b"\x04\x16", "Ж");
test_trans_simple!(trans_simple_chinese, "chinese", b"\xA7\xA8", "Ж");
test_trans_simple!(trans_simple_korean, "korean", b"\xAC\xA8", "Ж");
test_trans_simple!(trans_simple_big5_hkscs, "big5-hkscs", b"\xC7\xFA", "Ж");
test_trans_simple!(trans_simple_gbk, "gbk", b"\xA7\xA8", "Ж");
test_trans_simple!(trans_simple_sjis, "sjis", b"\x84\x47", "Ж");
test_trans_simple!(trans_simple_eucjp, "euc-jp", b"\xA7\xA8", "Ж");
test_trans_simple!(trans_simple_latin1, "latin1", b"\xA9", "©");
}

View File

@@ -2,6 +2,7 @@ extern crate atty;
extern crate bytecount; extern crate bytecount;
#[macro_use] #[macro_use]
extern crate clap; extern crate clap;
extern crate encoding_rs;
extern crate env_logger; extern crate env_logger;
extern crate grep; extern crate grep;
extern crate ignore; extern crate ignore;
@@ -43,6 +44,7 @@ macro_rules! eprintln {
mod app; mod app;
mod args; mod args;
mod decoder;
mod pathutil; mod pathutil;
mod printer; mod printer;
mod search_buffer; mod search_buffer;

View File

@@ -3,12 +3,32 @@ use std::fmt;
use std::path::Path; use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use regex::bytes::Regex; use regex::bytes::{Regex, Replacer, Captures};
use termcolor::{Color, ColorSpec, ParseColorError, WriteColor}; use termcolor::{Color, ColorSpec, ParseColorError, WriteColor};
use pathutil::strip_prefix; use pathutil::strip_prefix;
use ignore::types::FileTypeDef; use ignore::types::FileTypeDef;
/// CountingReplacer implements the Replacer interface for Regex,
/// and counts how often replacement is being performed.
struct CountingReplacer<'r> {
replace: &'r [u8],
count: &'r mut usize,
}
impl<'r> CountingReplacer<'r> {
fn new(replace: &'r [u8], count: &'r mut usize) -> CountingReplacer<'r> {
CountingReplacer { replace: replace, count: count }
}
}
impl<'r> Replacer for CountingReplacer<'r> {
fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>) {
*self.count += 1;
caps.expand(self.replace, dst);
}
}
/// Printer encapsulates all output logic for searching. /// Printer encapsulates all output logic for searching.
/// ///
/// Note that we currently ignore all write errors. It's probably worthwhile /// Note that we currently ignore all write errors. It's probably worthwhile
@@ -46,6 +66,8 @@ pub struct Printer<W> {
colors: ColorSpecs, colors: ColorSpecs,
/// The separator to use for file paths. If empty, this is ignored. /// The separator to use for file paths. If empty, this is ignored.
path_separator: Option<u8>, path_separator: Option<u8>,
/// Restrict lines to this many columns.
max_columns: Option<usize>
} }
impl<W: WriteColor> Printer<W> { impl<W: WriteColor> Printer<W> {
@@ -65,6 +87,7 @@ impl<W: WriteColor> Printer<W> {
with_filename: false, with_filename: false,
colors: ColorSpecs::default(), colors: ColorSpecs::default(),
path_separator: None, path_separator: None,
max_columns: None,
} }
} }
@@ -144,6 +167,12 @@ impl<W: WriteColor> Printer<W> {
self self
} }
/// Configure the max. number of columns used for printing matching lines.
pub fn max_columns(mut self, max_columns: Option<usize>) -> Printer<W> {
self.max_columns = max_columns;
self
}
/// Returns true if and only if something has been printed. /// Returns true if and only if something has been printed.
pub fn has_printed(&self) -> bool { pub fn has_printed(&self) -> bool {
self.has_printed self.has_printed
@@ -263,31 +292,57 @@ impl<W: WriteColor> Printer<W> {
self.write(b":"); self.write(b":");
} }
if self.replace.is_some() { if self.replace.is_some() {
let line = re.replace_all( let mut count = 0;
&buf[start..end], &**self.replace.as_ref().unwrap()); let line = {
let replacer = CountingReplacer::new(
self.replace.as_ref().unwrap(), &mut count);
re.replace_all(&buf[start..end], replacer)
};
if self.max_columns.map_or(false, |m| line.len() > m) {
let _ = self.wtr.set_color(self.colors.matched());
let msg = format!(
"[Omitted long line with {} replacements]", count);
self.write(msg.as_bytes());
let _ = self.wtr.reset();
self.write_eol();
return;
}
self.write(&line); self.write(&line);
if line.last() != Some(&self.eol) {
self.write_eol();
}
} else { } else {
self.write_matched_line(re, &buf[start..end]); self.write_matched_line(re, &buf[start..end]);
} // write_matched_line guarantees to write a newline.
if buf[start..end].last() != Some(&self.eol) {
self.write_eol();
} }
} }
fn write_matched_line(&mut self, re: &Regex, buf: &[u8]) { fn write_matched_line(&mut self, re: &Regex, buf: &[u8]) {
if !self.wtr.supports_color() || self.colors.matched().is_none() { if self.max_columns.map_or(false, |m| buf.len() > m) {
self.write(buf); let count = re.find_iter(buf).count();
let _ = self.wtr.set_color(self.colors.matched());
let msg = format!("[Omitted long line with {} matches]", count);
self.write(msg.as_bytes());
let _ = self.wtr.reset();
self.write_eol();
return; return;
} }
let mut last_written = 0; if !self.wtr.supports_color() || self.colors.matched().is_none() {
for m in re.find_iter(buf) { self.write(buf);
self.write(&buf[last_written..m.start()]); } else {
let _ = self.wtr.set_color(self.colors.matched()); let mut last_written = 0;
self.write(&buf[m.start()..m.end()]); for m in re.find_iter(buf) {
let _ = self.wtr.reset(); self.write(&buf[last_written..m.start()]);
last_written = m.end(); let _ = self.wtr.set_color(self.colors.matched());
self.write(&buf[m.start()..m.end()]);
let _ = self.wtr.reset();
last_written = m.end();
}
self.write(&buf[last_written..]);
}
if buf.last() != Some(&self.eol) {
self.write_eol();
} }
self.write(&buf[last_written..]);
} }
pub fn context<P: AsRef<Path>>( pub fn context<P: AsRef<Path>>(
@@ -312,6 +367,11 @@ 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 self.max_columns.map_or(false, |m| end - start > m) {
self.write(format!("[Omitted long context line]").as_bytes());
self.write_eol();
return;
}
self.write(&buf[start..end]); self.write(&buf[start..end]);
if buf[start..end].last() != Some(&self.eol) { if buf[start..end].last() != Some(&self.eol) {
self.write_eol(); self.write_eol();

View File

@@ -113,8 +113,8 @@ impl<'a, W: WriteColor> BufferSearcher<'a, W> {
#[inline(never)] #[inline(never)]
pub fn run(mut self) -> u64 { pub fn run(mut self) -> u64 {
let binary_upto = cmp::min(4096, self.buf.len()); let binary_upto = cmp::min(10240, self.buf.len());
if !self.opts.text && is_binary(&self.buf[..binary_upto]) { if !self.opts.text && is_binary(&self.buf[..binary_upto], true) {
return 0; return 0;
} }

View File

@@ -248,6 +248,7 @@ impl<'a, R: io::Read, W: WriteColor> Searcher<'a, R, W> {
/// If enabled, search binary files as if they were text. /// If enabled, search binary files as if they were text.
pub fn text(mut self, yes: bool) -> Self { pub fn text(mut self, yes: bool) -> Self {
self.opts.text = yes; self.opts.text = yes;
self.inp.text(yes);
self self
} }
@@ -266,9 +267,6 @@ impl<'a, R: io::Read, W: WriteColor> Searcher<'a, R, W> {
if !try!(self.fill()) { if !try!(self.fill()) {
break; break;
} }
if !self.opts.text && self.inp.is_binary {
break;
}
while !self.terminate() && self.inp.pos < self.inp.lastnl { while !self.terminate() && self.inp.pos < self.inp.lastnl {
let matched = self.grep.read_match( let matched = self.grep.read_match(
&mut self.last_match, &mut self.last_match,
@@ -501,10 +499,8 @@ pub struct InputBuffer {
end: usize, end: usize,
/// Set to true if and only if no reads have occurred yet. /// Set to true if and only if no reads have occurred yet.
first: bool, first: bool,
/// Set to true if and only if the contents of buf are determined to be /// Set to true if all binary data should be treated as if it were text.
/// "binary" (i.e., not searchable text). Note that its value may be text: bool,
/// falsely negative *or* falsely positive. It is only a heuristic.
is_binary: bool,
} }
impl InputBuffer { impl InputBuffer {
@@ -532,13 +528,23 @@ impl InputBuffer {
lastnl: 0, lastnl: 0,
end: 0, end: 0,
first: true, first: true,
is_binary: false, text: false,
} }
} }
/// Set the end-of-line terminator used by this input buffer. /// Set the end-of-line terminator used by this input buffer.
pub fn eol(&mut self, eol: u8) { pub fn eol(&mut self, eol: u8) -> &mut Self {
self.eol = eol; self.eol = eol;
self
}
/// If enabled, search binary files as if they were text.
///
/// Note that this may cause the buffer to load the entire contents of a
/// file into memory.
pub fn text(&mut self, yes: bool) -> &mut Self {
self.text = yes;
self
} }
/// Resets this buffer so that it may be reused with a new reader. /// Resets this buffer so that it may be reused with a new reader.
@@ -547,7 +553,6 @@ impl InputBuffer {
self.lastnl = 0; self.lastnl = 0;
self.end = 0; self.end = 0;
self.first = true; self.first = true;
self.is_binary = false;
} }
/// Fill the contents of this buffer with the reader given. The reader /// Fill the contents of this buffer with the reader given. The reader
@@ -582,8 +587,10 @@ impl InputBuffer {
} }
let n = try!(rdr.read( let n = try!(rdr.read(
&mut self.buf[self.end..self.end + self.read_size])); &mut self.buf[self.end..self.end + self.read_size]));
if self.first && is_binary(&self.buf[self.end..self.end + n]) { if !self.text {
self.is_binary = true; if is_binary(&self.buf[self.end..self.end + n], self.first) {
return Ok(false);
}
} }
self.first = false; self.first = false;
// We assume that reading 0 bytes means we've hit EOF. // We assume that reading 0 bytes means we've hit EOF.
@@ -613,11 +620,11 @@ impl InputBuffer {
/// ///
/// Note that this may return both false positives and false negatives. /// Note that this may return both false positives and false negatives.
#[inline(always)] #[inline(always)]
pub fn is_binary(buf: &[u8]) -> bool { pub fn is_binary(buf: &[u8], first: bool) -> bool {
if buf.len() >= 4 && &buf[0..4] == b"%PDF" { if first && buf.len() >= 4 && &buf[0..4] == b"%PDF" {
return true; return true;
} }
memchr(b'\x00', &buf[0..cmp::min(1024, buf.len())]).is_some() memchr(b'\x00', buf).is_some()
} }
/// Count the number of lines in the given buffer. /// Count the number of lines in the given buffer.

View File

@@ -2,11 +2,13 @@ use std::fs::File;
use std::io; use std::io;
use std::path::Path; use std::path::Path;
use encoding_rs::Encoding;
use grep::Grep; use grep::Grep;
use ignore::DirEntry; use ignore::DirEntry;
use memmap::{Mmap, Protection}; use memmap::{Mmap, Protection};
use termcolor::WriteColor; use termcolor::WriteColor;
use decoder::DecodeReader;
use pathutil::strip_prefix; use pathutil::strip_prefix;
use printer::Printer; use printer::Printer;
use search_buffer::BufferSearcher; use search_buffer::BufferSearcher;
@@ -27,6 +29,7 @@ pub struct WorkerBuilder {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct Options { struct Options {
mmap: bool, mmap: bool,
encoding: Option<&'static Encoding>,
after_context: usize, after_context: usize,
before_context: usize, before_context: usize,
count: bool, count: bool,
@@ -45,6 +48,7 @@ impl Default for Options {
fn default() -> Options { fn default() -> Options {
Options { Options {
mmap: false, mmap: false,
encoding: None,
after_context: 0, after_context: 0,
before_context: 0, before_context: 0,
count: false, count: false,
@@ -80,6 +84,7 @@ impl WorkerBuilder {
Worker { Worker {
grep: self.grep, grep: self.grep,
inpbuf: inpbuf, inpbuf: inpbuf,
decodebuf: vec![0; 8 * (1<<10)],
opts: self.opts, opts: self.opts,
} }
} }
@@ -106,6 +111,15 @@ impl WorkerBuilder {
self self
} }
/// Set the encoding to use to read each file.
///
/// If the encoding is `None` (the default), then the encoding is
/// automatically detected on a best-effort per-file basis.
pub fn encoding(mut self, enc: Option<&'static Encoding>) -> Self {
self.opts.encoding = enc;
self
}
/// If enabled, searching will print the path instead of each match. /// If enabled, searching will print the path instead of each match.
/// ///
/// Disabled by default. /// Disabled by default.
@@ -181,8 +195,9 @@ impl WorkerBuilder {
/// Worker is responsible for executing searches on file paths, while choosing /// Worker is responsible for executing searches on file paths, while choosing
/// streaming search or memory map search as appropriate. /// streaming search or memory map search as appropriate.
pub struct Worker { pub struct Worker {
inpbuf: InputBuffer,
grep: Grep, grep: Grep,
inpbuf: InputBuffer,
decodebuf: Vec<u8>,
opts: Options, opts: Options,
} }
@@ -241,6 +256,8 @@ impl Worker {
path: &Path, path: &Path,
rdr: R, rdr: R,
) -> Result<u64> { ) -> Result<u64> {
let rdr = DecodeReader::new(
rdr, &mut self.decodebuf, self.opts.encoding);
let searcher = Searcher::new( let searcher = Searcher::new(
&mut self.inpbuf, printer, &self.grep, path, rdr); &mut self.inpbuf, printer, &self.grep, path, rdr);
searcher searcher
@@ -274,8 +291,13 @@ impl Worker {
return self.search(printer, path, file); return self.search(printer, path, file);
} }
let mmap = try!(Mmap::open(file, Protection::Read)); let mmap = try!(Mmap::open(file, Protection::Read));
let searcher = BufferSearcher::new( let buf = unsafe { mmap.as_slice() };
printer, &self.grep, path, unsafe { mmap.as_slice() }); if buf.len() >= 3 && Encoding::for_bom(buf).is_some() {
// If we have a UTF-16 bom in our memory map, then we need to fall
// back to the stream reader, which will do transcoding.
return self.search(printer, path, file);
}
let searcher = BufferSearcher::new(printer, &self.grep, path, buf);
Ok(searcher Ok(searcher
.count(self.opts.count) .count(self.opts.count)
.files_with_matches(self.opts.files_with_matches) .files_with_matches(self.opts.files_with_matches)

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "termcolor" name = "termcolor"
version = "0.3.0" #:version version = "0.3.1" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"] authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """ description = """
A simple cross platform library for writing colored text to a terminal. A simple cross platform library for writing colored text to a terminal.
@@ -17,4 +17,4 @@ name = "termcolor"
bench = false bench = false
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
wincolor = { version = "0.1.1", path = "../wincolor" } wincolor = { version = "0.1.3", path = "../wincolor" }

View File

@@ -6,9 +6,6 @@ by interacting with the Windows console. Several convenient abstractions
are provided for use in single-threaded or multi-threaded command line are provided for use in single-threaded or multi-threaded command line
applications. applications.
[![Windows build status](https://ci.appveyor.com/api/projects/status/github/BurntSushi/ripgrep?svg=true)](https://ci.appveyor.com/project/BurntSushi/ripgrep)
[![](https://img.shields.io/crates/v/wincolor.svg)](https://crates.io/crates/wincolor)
[![Linux build status](https://api.travis-ci.org/BurntSushi/ripgrep.png)](https://travis-ci.org/BurntSushi/ripgrep) [![Linux build status](https://api.travis-ci.org/BurntSushi/ripgrep.png)](https://travis-ci.org/BurntSushi/ripgrep)
[![Windows build status](https://ci.appveyor.com/api/projects/status/github/BurntSushi/ripgrep?svg=true)](https://ci.appveyor.com/project/BurntSushi/ripgrep) [![Windows build status](https://ci.appveyor.com/api/projects/status/github/BurntSushi/ripgrep?svg=true)](https://ci.appveyor.com/project/BurntSushi/ripgrep)
[![](https://img.shields.io/crates/v/termcolor.svg)](https://crates.io/crates/termcolor) [![](https://img.shields.io/crates/v/termcolor.svg)](https://crates.io/crates/termcolor)
@@ -25,7 +22,7 @@ Add this to your `Cargo.toml`:
```toml ```toml
[dependencies] [dependencies]
termcolor = "0.1" termcolor = "0.3"
``` ```
and this to your crate root: and this to your crate root:

View File

@@ -432,6 +432,63 @@ sherlock!(context_line_numbers, "world|attached",
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
sherlock!(max_filesize_parse_error_length, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--max-filesize").arg("44444444444444444444");
wd.assert_err(&mut cmd);
});
sherlock!(max_filesize_parse_error_suffix, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--max-filesize").arg("45k");
wd.assert_err(&mut cmd);
});
sherlock!(max_filesize_parse_no_suffix, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
wd.remove("sherlock");
wd.create_size("foo", 40);
wd.create_size("bar", 60);
cmd.arg("--max-filesize").arg("50").arg("--files");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
foo
";
assert_eq!(lines, expected);
});
sherlock!(max_filesize_parse_k_suffix, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
wd.remove("sherlock");
wd.create_size("foo", 3048);
wd.create_size("bar", 4100);
cmd.arg("--max-filesize").arg("4K").arg("--files");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
foo
";
assert_eq!(lines, expected);
});
sherlock!(max_filesize_parse_m_suffix, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
wd.remove("sherlock");
wd.create_size("foo", 1000000);
wd.create_size("bar", 1400000);
cmd.arg("--max-filesize").arg("1M").arg("--files");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
foo
";
assert_eq!(lines, expected);
});
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);
@@ -988,6 +1045,64 @@ clean!(regression_279, "test", ".", |wd: WorkDir, mut cmd: Command| {
assert_eq!(lines, ""); assert_eq!(lines, "");
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/405
clean!(regression_405, "test", ".", |wd: WorkDir, mut cmd: Command| {
wd.create_dir("foo/bar");
wd.create_dir("bar/foo");
wd.create("foo/bar/file1.txt", "test");
wd.create("bar/foo/file2.txt", "test");
cmd.arg("-g").arg("!/foo/**");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, format!("{}:test\n", path("bar/foo/file2.txt")));
});
// See: https://github.com/BurntSushi/ripgrep/issues/1
clean!(feature_1_sjis, "Шерлок Холмс", ".", |wd: WorkDir, mut cmd: Command| {
let sherlock =
b"\x84Y\x84u\x84\x82\x84|\x84\x80\x84{ \x84V\x84\x80\x84|\x84}\x84\x83";
wd.create_bytes("foo", &sherlock[..]);
cmd.arg("-Esjis");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "foo:Шерлок Холмс\n");
});
// See: https://github.com/BurntSushi/ripgrep/issues/1
clean!(feature_1_utf16_auto, "Шерлок Холмс", ".",
|wd: WorkDir, mut cmd: Command| {
let sherlock =
b"\xff\xfe(\x045\x04@\x04;\x04>\x04:\x04 \x00%\x04>\x04;\x04<\x04A\x04";
wd.create_bytes("foo", &sherlock[..]);
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "foo:Шерлок Холмс\n");
});
// See: https://github.com/BurntSushi/ripgrep/issues/1
clean!(feature_1_utf16_explicit, "Шерлок Холмс", ".",
|wd: WorkDir, mut cmd: Command| {
let sherlock =
b"\xff\xfe(\x045\x04@\x04;\x04>\x04:\x04 \x00%\x04>\x04;\x04<\x04A\x04";
wd.create_bytes("foo", &sherlock[..]);
cmd.arg("-Eutf-16le");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "foo:Шерлок Холмс\n");
});
// See: https://github.com/BurntSushi/ripgrep/issues/1
clean!(feature_1_eucjp, "Шерлок Холмс", ".",
|wd: WorkDir, mut cmd: Command| {
let sherlock =
b"\xa7\xba\xa7\xd6\xa7\xe2\xa7\xdd\xa7\xe0\xa7\xdc \xa7\xb7\xa7\xe0\xa7\xdd\xa7\xde\xa7\xe3";
wd.create_bytes("foo", &sherlock[..]);
cmd.arg("-Eeuc-jp");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "foo:Шерлок Холмс\n");
});
// 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");
@@ -1199,6 +1314,36 @@ clean!(feature_109_case_sensitive_part2, "test", ".",
wd.assert_err(&mut cmd); wd.assert_err(&mut cmd);
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/129
clean!(feature_129_matches, "test", ".", |wd: WorkDir, mut cmd: Command| {
wd.create("foo", "test\ntest abcdefghijklmnopqrstuvwxyz test");
cmd.arg("-M26");
let lines: String = wd.stdout(&mut cmd);
let expected = "foo:test\nfoo:[Omitted long line with 2 matches]\n";
assert_eq!(lines, expected);
});
// See: https://github.com/BurntSushi/ripgrep/issues/129
clean!(feature_129_context, "test", ".", |wd: WorkDir, mut cmd: Command| {
wd.create("foo", "test\nabcdefghijklmnopqrstuvwxyz");
cmd.arg("-M20").arg("-C1");
let lines: String = wd.stdout(&mut cmd);
let expected = "foo:test\nfoo-[Omitted long context line]\n";
assert_eq!(lines, expected);
});
// See: https://github.com/BurntSushi/ripgrep/issues/129
clean!(feature_129_replace, "test", ".", |wd: WorkDir, mut cmd: Command| {
wd.create("foo", "test\ntest abcdefghijklmnopqrstuvwxyz test");
cmd.arg("-M26").arg("-rfoo");
let lines: String = wd.stdout(&mut cmd);
let expected = "foo:foo\nfoo:[Omitted long line with 2 replacements]\n";
assert_eq!(lines, expected);
});
// See: https://github.com/BurntSushi/ripgrep/issues/159 // See: https://github.com/BurntSushi/ripgrep/issues/159
clean!(feature_159_works, "test", ".", |wd: WorkDir, mut cmd: Command| { clean!(feature_159_works, "test", ".", |wd: WorkDir, mut cmd: Command| {
wd.create("foo", "test\ntest"); wd.create("foo", "test\ntest");
@@ -1326,6 +1471,27 @@ fn regression_270() {
assert_eq!(lines, path("foo:-test\n")); assert_eq!(lines, path("foo:-test\n"));
} }
// See: https://github.com/BurntSushi/ripgrep/issues/391
#[test]
fn regression_391() {
let wd = WorkDir::new("regression_391");
wd.create_dir(".git");
wd.create("lock", "");
wd.create("bar.py", "");
wd.create(".git/packed-refs", "");
wd.create(".git/description", "");
let mut cmd = wd.command();
cmd.arg("--no-ignore").arg("--hidden").arg("--follow").arg("--files")
.arg("--glob")
.arg("!{.git,node_modules,plugged}/**")
.arg("--glob")
.arg("*.{js,json,php,md,styl,scss,sass,pug,html,config,py,cpp,c,go,hs}");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "bar.py\n");
}
#[test] #[test]
fn type_list() { fn type_list() {
let wd = WorkDir::new("type_list"); let wd = WorkDir::new("type_list");

View File

@@ -46,6 +46,13 @@ impl WorkDir {
self.create_bytes(name, contents.as_bytes()); self.create_bytes(name, contents.as_bytes());
} }
/// Create a new file with the given name and size.
pub fn create_size<P: AsRef<Path>>(&self, name: P, filesize: u64) {
let path = self.dir.join(name);
let file = nice_err(&path, File::create(&path));
nice_err(&path, file.set_len(filesize));
}
/// Create a new file with the given name and contents in this directory. /// Create a new file with the given name and contents in this directory.
pub fn create_bytes<P: AsRef<Path>>(&self, name: P, contents: &[u8]) { pub fn create_bytes<P: AsRef<Path>>(&self, name: P, contents: &[u8]) {
let path = self.dir.join(name); let path = self.dir.join(name);

3
wincolor/COPYING Normal file
View File

@@ -0,0 +1,3 @@
This project is dual-licensed under the Unlicense and MIT licenses.
You may use this code under the terms of either license.

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "wincolor" name = "wincolor"
version = "0.1.2" #:version version = "0.1.3" #: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.

21
wincolor/LICENSE-MIT Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Andrew Gallant
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

24
wincolor/UNLICENSE Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>