Compare commits

..

43 Commits

Author SHA1 Message Date
Andrew Gallant
5a666b042d bump ripgrep, ignore, globset
The `ignore` and `globset` crates both got breaking changes in the
course of fixing #444, so increase 0.x to 0.(x+1).
2017-05-11 19:12:20 -04:00
Andrew Gallant
16109166fe changelog 0.5.2 2017-05-11 19:05:40 -04:00
Andrew Gallant
0b685c8429 deps: update clap to 2.24
Fixes #442
2017-05-08 19:24:11 -04:00
Eli Miller
d2c7a76a3c Add Powershell tips and autocompletion instructions 2017-05-08 19:23:41 -04:00
Chris MacNaughton
20f7d9b3a2 Add snapcraft.yaml
[Snapcraft](https://snapcraft.io/) makes Linux packaging very simple in a cross-distro
way. This adds the snapcraft.yaml file to setup a snap of ripgrep
2017-05-04 08:30:22 -04:00
Roman Proskuryakov
362abed44a Fix reiteration of the first found match with --only-mathing flag
Fixes #451
2017-04-21 08:11:55 -04:00
Andrew Gallant
c50b8b4125 Add better error messages for invalid globs.
This threads the original glob given by end users through all of the
glob parsing errors. This was slightly trickier than it might appear
because the gitignore implementation actually modifies the glob before
compiling it. So in order to get better glob error messages everywhere,
we need to track the original glob both in the glob parser and in the
higher-level abstractions in the `ignore` crate.

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

Kudos to @hsivonen for chirping about this!
2017-04-12 18:14:23 -04:00
Marc Tiehuis
66efbad871 Add dfa-size-limit and regex-size-limit arguments
Fixes #362.
2017-04-12 18:14:23 -04:00
Bryan Richter
1f2a9b0306 Add nix 2017-04-12 18:10:59 -04:00
Andrew Gallant
a45fe94240 update brew tap 2017-04-09 10:28:22 -04:00
Andrew Gallant
ac1c95a6d9 0.5.1 2017-04-09 09:47:00 -04:00
Andrew Gallant
685b431d80 bump deps 2017-04-09 09:46:37 -04:00
Andrew Gallant
487713aa34 bump ignore 2017-04-09 09:45:00 -04:00
Andrew Gallant
e300541701 changelog 0.5.1 2017-04-09 09:43:16 -04:00
Andrew Gallant
e9df420d2f Add ability to colorize column numbers.
Fixes #377
2017-04-09 09:08:49 -04:00
Andrew Gallant
201b4fc757 update man page 2017-04-09 08:49:19 -04:00
Roman Proskuryakov
90a11dec5e Add -o/--only-matching flag.
Currently, the `--only-matching` flag conflicts with the `--replace`
flag. In the future, this restriction may be relaxed.

Fixes #34
2017-04-09 08:47:35 -04:00
Douman
9456d95e8f Add short note on Windows Tips 2017-04-09 08:32:23 -04:00
Kevin K
0c298f60a6 updates clap and removes home rolled -h/--help distinction
This commit updates clap to v2.23.0

The update contained a bug fix in clap that results in broken code in
ripgrep. ripgrep was relying on the bug, but this commit fixes that
issue. The bug centered around not being able to override the
auto-generated help message by supplying a flag with a long of `help`.

Normally, supplying a flag with a long of `help` means whenever the user
passes `--help`, the consuming code (e.g. ripgrep) is responsible for
displaying the help message. However, due to the bug in clap this wasn't
necessary for ripgrep to do unless the user passed `-h`. With the bug
fixed, it meant the user passing `--help` and clap expected ripgrep to
display the help, yet ripgrep expected clap to display the help. This
has been fixed in this commit of ripgrep.

All well now!

v2.23.0 also brings the abilty to use `Arg::help` or `Arg::long_help`
allowing one to distinguish between `-h` and `--help`. This commit
leaves all doc strings in the `lazy_static!` hashmap however only for
aesthetic reasons.

This means all home rolled handling of `-h`/`--help` has been removed
from ripgrep, yet functionality *and* appearances are 100% the same.
2017-04-05 11:38:58 -04:00
Kosta Welke
79271fcb33 fix typo in comment 2017-04-05 07:06:58 -04:00
Andrew Gallant
fc975af8e9 Enforce 79 column limit. Grr. 2017-03-31 15:59:04 -04:00
Roman Proskuryakov
1425d6735e Bamp clap to 2.22.2
Fixes #426 , #418
2017-03-31 15:56:10 -04:00
Roman Proskuryakov
aed3ccb9c7 Improves Printer, fixes some bugs 2017-03-31 14:44:13 -04:00
Andrew Gallant
33c95d2919 bump deps 2017-03-30 12:33:31 -04:00
Roman Proskuryakov
01deac9427 Add -0 shortcut for --null
Fixes #419
2017-03-28 18:37:40 -04:00
Andrew Gallant
b4bc3b6349 remove uninstall step 2017-03-28 12:14:15 -04:00
Andrew Gallant
685cc6c562 Add vim type.
It's the same as the vimscript type, but shorter and more obvious.

Fixes #415
2017-03-21 07:56:49 -04:00
Andrew Gallant
08c017330f bump termcolor dep 2017-03-15 07:15:39 -04:00
Andrew Gallant
2f3a8c7f69 termcolor-0.3.2 2017-03-15 06:58:09 -04:00
Andrew Gallant
3ac1b68e54 Add license info to termcolor crate.
Fixes #381
2017-03-15 06:57:54 -04:00
Andrew Gallant
0ebd5465b7 remove allow(dead_code) 2017-03-14 15:09:24 -04:00
Andrew Gallant
5cb4bb9ea0 bump ripgrep version in Cargo.lock 2017-03-14 15:09:24 -04:00
Leaf Garland
c8a179b4da Add powershell completions to build artifacts
Use a `ps:` (powershell) command to copy the completions file so that we
can use directory globbing to find the file.
2017-03-14 08:53:04 -04:00
Andrew Gallant
46f94826fd Update whirlwind tour with encoding info.
Fixes #1
2017-03-14 08:22:37 -04:00
Andrew Gallant
75f1855a91 Fix brew tap sha256 sum.
Fixes #407
2017-03-13 06:50:45 -04:00
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
30 changed files with 915 additions and 302 deletions

8
.gitignore vendored
View File

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

View File

@@ -1,3 +1,106 @@
0.5.2 (2017-05-11)
==================
Feature enhancements:
* Added or improved file type filtering for Nix.
* [FEATURE #362](https://github.com/BurntSushi/ripgrep/issues/362):
Add `--regex-size-limit` and `--dfa-size-limit` flags.
* [FEATURE #444](https://github.com/BurntSushi/ripgrep/issues/444):
Improve error messages for invalid globs.
Bug fixes:
* [BUG #442](https://github.com/BurntSushi/ripgrep/issues/442):
Fix line wrapping in `--help` output.
* [BUG #451](https://github.com/BurntSushi/ripgrep/issues/451):
Fix bug with duplicate output when using `-o/--only-matching` flag.
0.5.1 (2017-04-09)
==================
Feature enhancements:
* Added or improved file type filtering for vim.
* [FEATURE #34](https://github.com/BurntSushi/ripgrep/issues/34):
Add a `-o/--only-matching` flag.
* [FEATURE #377](https://github.com/BurntSushi/ripgrep/issues/377):
Column numbers can now be customized with a color. (The default is
no color.)
* [FEATURE #419](https://github.com/BurntSushi/ripgrep/issues/419):
Added `-0` short flag option for `--null`.
Bug fixes:
* [BUG #381](https://github.com/BurntSushi/ripgrep/issues/381):
Include license text in all subcrates.
* [BUG #418](https://github.com/BurntSushi/ripgrep/issues/418),
[BUG #426](https://github.com/BurntSushi/ripgrep/issues/426),
[BUG #439](https://github.com/BurntSushi/ripgrep/issues/439):
Fix a few bugs with `-h/--help` output.
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

80
Cargo.lock generated
View File

@@ -1,28 +1,28 @@
[root] [root]
name = "ripgrep" name = "ripgrep"
version = "0.4.0" version = "0.5.2"
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.21.1 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.24.1 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding_rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"grep 0.1.6", "grep 0.1.6",
"ignore 0.1.8", "ignore 0.2.0",
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.4.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.3 (registry+https://github.com/rust-lang/crates.io-index)", "same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 0.3.1", "termcolor 0.3.2",
] ]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.6.2" version = "0.6.3"
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)",
@@ -39,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.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "0.8.0" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -63,14 +63,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.21.1" version = "2.24.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)",
"atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "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.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -83,7 +83,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.5.0" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -108,15 +108,15 @@ 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.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "globset" name = "globset"
version = "0.1.4" version = "0.2.0"
dependencies = [ dependencies = [
"aho-corasick 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.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)",
@@ -135,11 +135,11 @@ dependencies = [
[[package]] [[package]]
name = "ignore" name = "ignore"
version = "0.1.8" version = "0.2.0"
dependencies = [ dependencies = [
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"globset 0.1.4", "globset 0.2.0",
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.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)",
@@ -158,12 +158,12 @@ dependencies = [
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "0.2.4" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.21" version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -176,7 +176,7 @@ name = "memchr"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -186,16 +186,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"fs2 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "fs2 0.4.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.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.3.0" version = "1.4.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.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -203,7 +203,7 @@ 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.2 (registry+https://github.com/rust-lang/crates.io-index)", "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.4.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)",
@@ -237,17 +237,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "term_size" name = "term_size"
version = "0.2.3" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (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.3.1" version = "0.3.2"
dependencies = [ dependencies = [
"wincolor 0.1.3", "wincolor 0.1.3",
] ]
@@ -258,7 +258,7 @@ 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.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@@ -332,31 +332,31 @@ dependencies = [
] ]
[metadata] [metadata]
"checksum aho-corasick 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0638fd549427caa90c499814196d1b9e3725eb4d15d7339d6de073a680ed0ca2" "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" "checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
"checksum bitflags 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e1ab483fc81a8143faa7203c4a3c02888ebd1a782e37e41fa34753ba9a162" "checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
"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 cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" "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 clap 2.24.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7541069be0b8aec41030802abe8b5cdef0490070afaa55418adea93b1e431e0"
"checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97"
"checksum encoding_rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a1cca0a26f904955d80d70b9bff1019e4f4cbc06f2fcbccf8bd3d889cc1c9b7" "checksum encoding_rs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e4bc519d572af08cf72c4d61e0de9b05e9fa66d1fdb5e739fb5c405860b42d43"
"checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83" "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.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34edaee07555859dc13ca387e6ae05686bb4d0364c95d649b6dab959511f4baf" "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.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7291b1dd97d331f752620b02dfdbc231df7fc01bf282a00769e1cdb963c460dc" "checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
"checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135" "checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502"
"checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad" "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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46f3c7359028b31999287dae4e5047ddfe90a23b7dca2282ce759b491080c99b" "checksum memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46f3c7359028b31999287dae4e5047ddfe90a23b7dca2282ce759b491080c99b"
"checksum num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a18c392466409c50b87369414a2680c93e739aedeb498eb2bff7d7eb569744e2" "checksum num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca313f1862c7ec3e0dfe8ace9fa91b1d9cb5c84ace3d00f5ec4216238e93c167"
"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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" "checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
"checksum simd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63b5847c2d766ca7ce7227672850955802fabd779ba616aeabead4c2c3877023" "checksum simd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63b5847c2d766ca7ce7227672850955802fabd779ba616aeabead4c2c3877023"
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
"checksum term_size 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "07b6c1ac5b3fffd75073276bca1ceed01f67a28537097a2a9539e116e50fb21a" "checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
"checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" "checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a"
"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7" "checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7"
"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3" "checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "ripgrep" name = "ripgrep"
version = "0.4.0" #:version version = "0.5.2" #: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,11 +28,11 @@ path = "tests/tests.rs"
[dependencies] [dependencies]
atty = "0.2.2" atty = "0.2.2"
bytecount = "0.1.4" bytecount = "0.1.4"
clap = "2.20.5" clap = "2.24.1"
encoding_rs = "0.5.0" encoding_rs = "0.5.0"
env_logger = { version = "0.4", default-features = false } env_logger = { version = "0.4", default-features = false }
grep = { version = "0.1.5", path = "grep" } grep = { version = "0.1.5", path = "grep" }
ignore = { version = "0.1.7", path = "ignore" } ignore = { version = "0.2.0", path = "ignore" }
lazy_static = "0.2" lazy_static = "0.2"
libc = "0.2" libc = "0.2"
log = "0.3" log = "0.3"
@@ -44,7 +44,7 @@ same-file = "0.1.1"
termcolor = { version = "0.3.0", path = "termcolor" } termcolor = { version = "0.3.0", path = "termcolor" }
[build-dependencies] [build-dependencies]
clap = "2.18" clap = "2.24.1"
lazy_static = "0.2" lazy_static = "0.2"
[features] [features]

103
README.md
View File

@@ -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)
@@ -226,11 +230,10 @@ colorize your output and show line numbers, just like The Silver Searcher.
Coloring works on Windows too! Colors can be controlled more granularly with Coloring works on Windows too! Colors can be controlled more granularly with
the `--color` flag. the `--color` flag.
One last thing before we get started: `ripgrep` assumes UTF-8 *everywhere*. It One last thing before we get started: generally speaking, `ripgrep` assumes the
can still search files that are invalid UTF-8 (like, say, latin-1), but it will input is reading is UTF-8. However, if ripgrep notices a file is encoded as
simply not work on UTF-16 encoded files or other more exotic encodings. UTF-16, then it will know how to search it. For other encodings, you'll need to
[Support for other encodings may explicitly specify them with the `-E/--encoding` flag.
happen.](https://github.com/BurntSushi/ripgrep/issues/1)
To recursively search the current directory, while respecting all `.gitignore` To recursively search the current directory, while respecting all `.gitignore`
files, ignore hidden files and directories and skip binary files: files, ignore hidden files and directories and skip binary files:
@@ -340,6 +343,11 @@ or `/etc/bash_completion.d/`.
For **fish**, move `rg.fish` to `$HOME/.config/fish/completions`. For **fish**, move `rg.fish` to `$HOME/.config/fish/completions`.
For **PowerShell**, add `. _rg.ps1` to your PowerShell
[profile](https://technet.microsoft.com/en-us/library/bb613488(v=vs.85).aspx)
(note the leading period). If the `_rg.ps1` file is not on your `PATH`, do
`. /path/to/_rg.ps1` instead.
### Building ### Building
`ripgrep` is written in Rust, so you'll need to grab a `ripgrep` is written in Rust, so you'll need to grab a
@@ -375,12 +383,93 @@ $ cargo test
from the repository root. from the repository root.
### Tips
#### Windows Powershell
##### Powershell Profile
To customize powershell on start-up there is a special powershell script that has to be created.
In order to find its location run command `Get-Command $profile | Select-Object -ExpandProperty Definition`
See [more](https://technet.microsoft.com/en-us/library/bb613488(v=vs.85).aspx) for profile details.
Any powershell code in this file gets evaluated at the start of console.
This way you can have own aliases to be created at start.
##### Setup function alias
Often you can find a need to make alias for the favourite utility.
But powershell function aliases do not behave like your typical linux shell alias.
You always need to propagate arguments and **Stdin** input.
But it cannot be done simply as `function grep() { $input | rg.exe --hidden $args }`
Use below example as reference to how setup alias in powershell.
```powershell
function grep {
$count = @($input).Count
$input.Reset()
if ($count) {
$input | rg.exe --hidden $args
}
else {
rg.exe --hidden $args
}
}
```
Powershell special variables:
* input - is powershell **Stdin** object that allows you to access its content.
* args - is array of arguments passed to this function.
This alias checks whether there is **Stdin** input and propagates only if there is some lines.
Otherwise empty `$input` will make powershell to trigger `rg` to search empty **Stdin**
##### Piping non-ASCII content to ripgrep
When piping input into native executables in PowerShell, the encoding of the
input is controlled by the `$OutputEncoding` variable. By default, this is set
to US-ASCII, and any characters in the pipeline that don't have encodings in
US-ASCII are converted to `?` (question mark) characters.
To change this setting, set `$OutputEncoding` to a different encoding, as
represented by a .NET encoding object. Some common examples are below. The
value of this variable is reset when PowerShell restarts, so to make this
change take effect every time PowerShell is started add a line setting the
variable into your PowerShell profile.
Example `$OutputEncoding` settings:
* UTF-8 without BOM: `$OutputEncoding = [System.Text.UTF8Encoding]::new()`
* The console's output encoding:
`$OutputEncoding = [System.Console]::OutputEncoding`
If you continue to have encoding problems, you can also force the encoding
that the console will use for printing to UTF-8 with
`[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8`. This
will also reset when PowerShell is restarted, so you can add that line
to your profile as well if you want to make the setting permanent.
### Known issues ### Known issues
#### I just hit Ctrl+C in the middle of ripgrep's output and now my terminal's foreground color is wrong! #### I just hit Ctrl+C in the middle of ripgrep's output and now my terminal's foreground color is wrong!
Type in `color` on Windows and `echo -ne "\033[0m"` on Unix to restore your Type in `color` in cmd.exe (Command Prompt) and `echo -ne "\033[0m"` on Unix
original foreground color. to restore your original foreground color.
In PowerShell, you can add the following code to your profile which will
restore the original foreground color when `Reset-ForegroundColor` is called.
Including the `Set-Alias` line will allow you to call it with simply `color`.
```powershell
$OrigFgColor = $Host.UI.RawUI.ForegroundColor
function Reset-ForegroundColor {
$Host.UI.RawUI.ForegroundColor = $OrigFgColor
}
Set-Alias -Name color -Value Reset-ForegroundColor
```
PR [#187](https://github.com/BurntSushi/ripgrep/pull/187) fixed this, and it PR [#187](https://github.com/BurntSushi/ripgrep/pull/187) fixed this, and it
was later deprecated in was later deprecated in

View File

@@ -40,7 +40,7 @@ before_deploy:
- cargo build --release - cargo build --release
- mkdir staging - mkdir staging
- copy target\release\rg.exe staging - copy target\release\rg.exe staging
- copy target\release\build\ripgrep-*\out\_rg.ps1 staging - ps: copy target\release\build\ripgrep-*\out\_rg.ps1 staging
- cd staging - cd staging
# release zipfile will look like 'rust-everywhere-v1.2.3-x86_64-pc-windows-msvc' # release zipfile will look like 'rust-everywhere-v1.2.3-x86_64-pc-windows-msvc'
- 7z a ../%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip * - 7z a ../%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip *

View File

@@ -19,7 +19,7 @@ fn main() {
}; };
fs::create_dir_all(&outdir).unwrap(); fs::create_dir_all(&outdir).unwrap();
let mut app = app::app_short(); let mut app = app::app();
app.gen_completions("rg", Shell::Bash, &outdir); app.gen_completions("rg", Shell::Bash, &outdir);
app.gen_completions("rg", Shell::Fish, &outdir); app.gen_completions("rg", Shell::Fish, &outdir);
app.gen_completions("rg", Shell::Zsh, &outdir); app.gen_completions("rg", Shell::Zsh, &outdir);

View File

@@ -17,9 +17,6 @@ install_c_toolchain() {
} }
install_rustup() { install_rustup() {
# uninstall the rust toolchain installed by travis, we are going to use rustup
sh ~/rust/lib/rustlib/uninstall.sh
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain=$TRAVIS_RUST_VERSION curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain=$TRAVIS_RUST_VERSION
rustc -V rustc -V

View File

@@ -181,7 +181,7 @@ Styles are limited to nobold, bold, nointense or intense.
.RS .RS
.PP .PP
The format of the flag is {type}:{attribute}:{value}. The format of the flag is {type}:{attribute}:{value}.
{type} should be one of path, line or match. {type} should be one of path, line, column or match.
{attribute} can be fg, bg or style. {attribute} can be fg, bg or style.
Value is either a color (for fg and bg) or a text style. Value is either a color (for fg and bg) or a text style.
A special format, {type}:none, will clear all color settings for {type}. A special format, {type}:none, will clear all color settings for {type}.
@@ -299,6 +299,13 @@ Follow symlinks.
.RS .RS
.RE .RE
.TP .TP
.B \-M, \-\-max\-columns \f[I]NUM\f[]
Don\[aq]t print lines longer than this limit in bytes.
Longer lines are omitted, and only the number of matches in that line is
printed.
.RS
.RE
.TP
.B \-m, \-\-max\-count \f[I]NUM\f[] .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
@@ -355,7 +362,7 @@ Note that .ignore files will continue to be respected.
.RS .RS
.RE .RE
.TP .TP
.B \-\-null .B \-0, \-\-null
Whenever a file name is printed, follow it with a NUL byte. Whenever a file name is printed, follow it with a NUL byte.
This includes printing filenames before matches, and when printing a This includes printing filenames before matches, and when printing a
list of matching files such as with \-\-count, \-\-files\-with\-matches list of matching files such as with \-\-count, \-\-files\-with\-matches
@@ -363,6 +370,12 @@ and \-\-files.
.RS .RS
.RE .RE
.TP .TP
.B \-o, \-\-only\-matching
Print only the matched (non\-empty) parts of a matching line, with each
such part on a separate output line.
.RS
.RE
.TP
.B \-\-path\-separator \f[I]SEPARATOR\f[] .B \-\-path\-separator \f[I]SEPARATOR\f[]
The path separator to use when printing file paths. The path separator to use when printing file paths.
This defaults to your platform\[aq]s path separator, which is / on Unix This defaults to your platform\[aq]s path separator, which is / on Unix

View File

@@ -123,9 +123,9 @@ Project home page: https://github.com/BurntSushi/ripgrep
black. Styles are limited to nobold, bold, nointense or intense. black. Styles are limited to nobold, bold, nointense or intense.
The format of the flag is {type}:{attribute}:{value}. {type} should be one The format of the flag is {type}:{attribute}:{value}. {type} should be one
of path, line or match. {attribute} can be fg, bg or style. Value is either of path, line, column or match. {attribute} can be fg, bg or style. Value
a color (for fg and bg) or a text style. A special format, {type}:none, is either a color (for fg and bg) or a text style. A special format,
will clear all color settings for {type}. {type}:none, will clear all color settings for {type}.
For example, the following command will change the match color to magenta For example, the following command will change the match color to magenta
and the background color for line numbers to yellow: and the background color for line numbers to yellow:
@@ -243,12 +243,16 @@ Project home page: https://github.com/BurntSushi/ripgrep
: Don't respect version control ignore files (e.g., .gitignore). : Don't respect version control ignore files (e.g., .gitignore).
Note that .ignore files will continue to be respected. Note that .ignore files will continue to be respected.
--null -0, --null
: Whenever a file name is printed, follow it with a NUL byte. : Whenever a file name is printed, follow it with a NUL byte.
This includes printing filenames before matches, and when printing This includes printing filenames before matches, and when printing
a list of matching files such as with --count, --files-with-matches a list of matching files such as with --count, --files-with-matches
and --files. and --files.
-o, --only-matching
: Print only the matched (non-empty) parts of a matching line, with each such
part on a separate output line.
--path-separator *SEPARATOR* --path-separator *SEPARATOR*
: The path separator to use when printing file paths. This defaults to your : The path separator to use when printing file paths. This defaults to your
platform's path separator, which is / on Unix and \\ on Windows. This flag is platform's path separator, which is / on Unix and \\ on Windows. This flag is

View File

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

View File

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

View File

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

View File

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

View File

@@ -279,7 +279,12 @@ impl GitignoreBuilder {
let nignore = self.globs.iter().filter(|g| !g.is_whitelist()).count(); let nignore = self.globs.iter().filter(|g| !g.is_whitelist()).count();
let nwhite = self.globs.iter().filter(|g| g.is_whitelist()).count(); let nwhite = self.globs.iter().filter(|g| g.is_whitelist()).count();
let set = try!( let set = try!(
self.builder.build().map_err(|err| Error::Glob(err.to_string()))); self.builder.build().map_err(|err| {
Error::Glob {
glob: None,
err: err.to_string(),
}
}));
Ok(Gitignore { Ok(Gitignore {
set: set, set: set,
root: self.root.clone(), root: self.root.clone(),
@@ -420,7 +425,12 @@ impl GitignoreBuilder {
GlobBuilder::new(&glob.actual) GlobBuilder::new(&glob.actual)
.literal_separator(literal_separator) .literal_separator(literal_separator)
.build() .build()
.map_err(|err| Error::Glob(err.to_string()))); .map_err(|err| {
Error::Glob {
glob: Some(glob.original.clone()),
err: err.kind().to_string(),
}
}));
self.builder.add(parsed); self.builder.add(parsed);
self.globs.push(glob); self.globs.push(glob);
Ok(self) Ok(self)

View File

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

View File

@@ -155,6 +155,7 @@ const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
("mk", &["mkfile"]), ("mk", &["mkfile"]),
("ml", &["*.ml"]), ("ml", &["*.ml"]),
("nim", &["*.nim"]), ("nim", &["*.nim"]),
("nix", &["*.nix"]),
("objc", &["*.h", "*.m"]), ("objc", &["*.h", "*.m"]),
("objcpp", &["*.h", "*.mm"]), ("objcpp", &["*.h", "*.mm"]),
("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]), ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
@@ -191,6 +192,7 @@ const DEFAULT_TYPES: &'static [(&'static str, &'static [&'static str])] = &[
("twig", &["*.twig"]), ("twig", &["*.twig"]),
("vala", &["*.vala"]), ("vala", &["*.vala"]),
("vb", &["*.vb"]), ("vb", &["*.vb"]),
("vim", &["*.vim"]),
("vimscript", &["*.vim"]), ("vimscript", &["*.vim"]),
("wiki", &["*.mediawiki", "*.wiki"]), ("wiki", &["*.mediawiki", "*.wiki"]),
("xml", &["*.xml"]), ("xml", &["*.xml"]),
@@ -446,13 +448,18 @@ impl TypesBuilder {
GlobBuilder::new(glob) GlobBuilder::new(glob)
.literal_separator(true) .literal_separator(true)
.build() .build()
.map_err(|err| Error::Glob(err.to_string())))); .map_err(|err| {
Error::Glob {
glob: Some(glob.to_string()),
err: err.kind().to_string(),
}
})));
glob_to_selection.push((isel, iglob)); glob_to_selection.push((isel, iglob));
} }
selections.push(selection.clone().map(move |_| def)); selections.push(selection.clone().map(move |_| def));
} }
let set = try!(build_set.build().map_err(|err| { let set = try!(build_set.build().map_err(|err| {
Error::Glob(err.to_string()) Error::Glob { glob: None, err: err.to_string() }
})); }));
Ok(Types { Ok(Types {
defs: defs, defs: defs,

View File

@@ -1,9 +1,9 @@
class RipgrepBin < Formula class RipgrepBin < Formula
version '0.4.0' version '0.5.1'
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 "517e40823f151ceb85840c8f147206360ee093aecbe96de20be4c2d5517d51ca"
conflicts_with "ripgrep" conflicts_with "ripgrep"

15
snapcraft.yaml Normal file
View File

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

View File

@@ -32,16 +32,6 @@ ARGS:
OPTIONS: OPTIONS:
{unified}"; {unified}";
/// Build a clap application with short help strings.
pub fn app_short() -> App<'static, 'static> {
app(false, |k| USAGES[k].short)
}
/// Build a clap application with long help strings.
pub fn app_long() -> App<'static, 'static> {
app(true, |k| USAGES[k].long)
}
/// Build a clap application parameterized by usage strings. /// Build a clap application parameterized by usage strings.
/// ///
/// The function given should take a clap argument name and return a help /// The function given should take a clap argument name and return a help
@@ -49,10 +39,11 @@ pub fn app_long() -> App<'static, 'static> {
/// ///
/// This is an intentionally stand-alone module so that it can be used easily /// This is an intentionally stand-alone module so that it can be used easily
/// in a `build.rs` script to build shell completion files. /// in a `build.rs` script to build shell completion files.
fn app<F>(next_line_help: bool, doc: F) -> App<'static, 'static> pub fn app() -> App<'static, 'static> {
where F: Fn(&'static str) -> &'static str {
let arg = |name| { let arg = |name| {
Arg::with_name(name).help(doc(name)).next_line_help(next_line_help) Arg::with_name(name)
.help(USAGES[name].short)
.long_help(USAGES[name].long)
}; };
let flag = |name| arg(name).long(name); let flag = |name| arg(name).long(name);
@@ -64,16 +55,12 @@ fn app<F>(next_line_help: bool, doc: F) -> App<'static, 'static>
.setting(AppSettings::UnifiedHelpMessage) .setting(AppSettings::UnifiedHelpMessage)
.usage(USAGE) .usage(USAGE)
.template(TEMPLATE) .template(TEMPLATE)
// Handle help/version manually to make their output formatting .help_message("Prints help information. Use --help for more details.")
// consistent with short/long views.
.arg(arg("help-short").short("h"))
.arg(flag("help"))
.arg(flag("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")
@@ -129,6 +116,8 @@ fn app<F>(next_line_help: bool, doc: F) -> App<'static, 'static>
.arg(flag("column")) .arg(flag("column"))
.arg(flag("context-separator") .arg(flag("context-separator")
.value_name("SEPARATOR").takes_value(true)) .value_name("SEPARATOR").takes_value(true))
.arg(flag("dfa-size-limit")
.value_name("NUM+SUFFIX?").takes_value(true))
.arg(flag("debug")) .arg(flag("debug"))
.arg(flag("file").short("f") .arg(flag("file").short("f")
.value_name("FILE").takes_value(true) .value_name("FILE").takes_value(true)
@@ -158,10 +147,13 @@ fn app<F>(next_line_help: bool, doc: F) -> App<'static, 'static>
.arg(flag("no-ignore")) .arg(flag("no-ignore"))
.arg(flag("no-ignore-parent")) .arg(flag("no-ignore-parent"))
.arg(flag("no-ignore-vcs")) .arg(flag("no-ignore-vcs"))
.arg(flag("null")) .arg(flag("null").short("0"))
.arg(flag("only-matching").short("o").conflicts_with("replace"))
.arg(flag("path-separator").value_name("SEPARATOR").takes_value(true)) .arg(flag("path-separator").value_name("SEPARATOR").takes_value(true))
.arg(flag("pretty").short("p")) .arg(flag("pretty").short("p"))
.arg(flag("replace").short("r").value_name("ARG").takes_value(true)) .arg(flag("replace").short("r").value_name("ARG").takes_value(true))
.arg(flag("regex-size-limit")
.value_name("NUM+SUFFIX?").takes_value(true))
.arg(flag("case-sensitive").short("s")) .arg(flag("case-sensitive").short("s"))
.arg(flag("smart-case").short("S")) .arg(flag("smart-case").short("S"))
.arg(flag("sort-files")) .arg(flag("sort-files"))
@@ -206,7 +198,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,12 +241,12 @@ lazy_static! {
red, blue, green, cyan, magenta, yellow, white and black. \ red, blue, green, cyan, magenta, yellow, white and black. \
Styles are limited to nobold, bold, nointense or intense.\n\n\ Styles are limited to nobold, bold, nointense or intense.\n\n\
The format of the flag is {type}:{attribute}:{value}. {type} \ The format of the flag is {type}:{attribute}:{value}. {type} \
should be one of path, line or match. {attribute} can be fg, bg \ should be one of path, line, column or match. {attribute} can \
or style. {value} is either a color (for fg and bg) or a text \ be fg, bg or style. {value} is either a color (for fg and bg) \
style. A special format, {type}:none, will clear all color \ or a text style. A special format, {type}:none, will clear all \
settings for {type}.\n\nFor example, the following command will \ color settings for {type}.\n\nFor example, the following \
change the match color to magenta and the background color for \ command will change the match color to magenta and the \
line numbers to yellow:\n\n\ background color for 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", doc!(h, "encoding",
"Specify the text encoding of files to search.", "Specify the text encoding of files to search.",
@@ -340,6 +332,13 @@ lazy_static! {
doc!(h, "debug", doc!(h, "debug",
"Show debug messages.", "Show debug messages.",
"Show debug messages. Please use this when filing a bug report."); "Show debug messages. Please use this when filing a bug report.");
doc!(h, "dfa-size-limit",
"The upper size limit of the generated dfa.",
"The upper size limit of the generated dfa. The default limit is \
10M. This should only be changed on very large regex inputs \
where the (slower) fallback regex engine may otherwise be used. \
\n\nThe argument accepts the same size suffixes as allowed in \
the 'max-filesize' argument.");
doc!(h, "file", doc!(h, "file",
"Search for patterns from the given file.", "Search for patterns from the given file.",
"Search for patterns from the given file, with one pattern per \ "Search for patterns from the given file, with one pattern per \
@@ -388,10 +387,11 @@ lazy_static! {
"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", doc!(h, "max-filesize",
"Ignore files larger than NUM in size.", "Ignore files larger than NUM in size.",
"Ignore files larger than NUM in size. Does not ignore directories. \ "Ignore files larger than NUM in size. Does not ignore \
directories. \
\n\nThe input format accepts suffixes of K, M or G which \ \n\nThe input format accepts suffixes of K, M or G which \
correspond to kilobytes, megabytes and gigabytes. If no suffix is \ correspond to kilobytes, megabytes and gigabytes. If no suffix \
provided the input is treated as bytes. \ is provided the input is treated as bytes. \
\n\nExample: --max-filesize 50K or --max-filesize 80M"); \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.",
@@ -435,6 +435,10 @@ lazy_static! {
printing a list of matching files such as with --count, \ printing a list of matching files such as with --count, \
--files-with-matches and --files. This option is useful for use \ --files-with-matches and --files. This option is useful for use \
with xargs."); with xargs.");
doc!(h, "only-matching",
"Print only matched parts of a line.",
"Print only the matched (non-empty) parts of a matching line, \
with each such part on a separate output line.");
doc!(h, "path-separator", doc!(h, "path-separator",
"Path separator to use when printing file paths.", "Path separator to use when printing file paths.",
"The path separator to use when printing file paths. This \ "The path separator to use when printing file paths. This \
@@ -453,6 +457,11 @@ lazy_static! {
Note that the replacement by default replaces each match, and \ Note that the replacement by default replaces each match, and \
NOT the entire line. To replace the entire line, you should \ NOT the entire line. To replace the entire line, you should \
match the entire line."); match the entire line.");
doc!(h, "regex-size-limit",
"The upper size limit of the compiled regex.",
"The upper size limit of the compiled regex. The default limit \
is 10M. \n\nThe argument accepts the same size suffixes as \
allowed in the 'max-filesize' argument.");
doc!(h, "case-sensitive", doc!(h, "case-sensitive",
"Search case sensitively.", "Search case sensitively.",
"Search case sensitively. This overrides -i/--ignore-case and \ "Search case sensitively. This overrides -i/--ignore-case and \
@@ -496,8 +505,8 @@ lazy_static! {
permits specifying one or more other type names (separated by a \ permits specifying one or more other type names (separated by a \
comma) that have been defined and its rules will automatically \ comma) that have been defined and its rules will automatically \
be imported into the type specified. For example, to create a \ be imported into the type specified. For example, to create a \
type called src that matches C++, Python and Markdown files, one \ type called src that matches C++, Python and Markdown files, \
can use:\n\n\ one can use:\n\n\
--type-add 'src:include:cpp,py,md'\n\n\ --type-add 'src:include:cpp,py,md'\n\n\
Additional glob rules can still be added to the src type by \ Additional glob rules can still be added to the src type by \
using the --type-add flag again:\n\n\ using the --type-add flag again:\n\n\

View File

@@ -5,7 +5,6 @@ use std::fs;
use std::io::{self, BufRead}; use std::io::{self, BufRead};
use std::ops; use std::ops;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@@ -66,6 +65,7 @@ pub struct Args {
no_ignore_vcs: bool, no_ignore_vcs: bool,
no_messages: bool, no_messages: bool,
null: bool, null: bool,
only_matching: bool,
path_separator: Option<u8>, path_separator: Option<u8>,
quiet: bool, quiet: bool,
quiet_matched: QuietMatched, quiet_matched: QuietMatched,
@@ -88,21 +88,7 @@ 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(); let matches = app::app().get_matches();
if matches.is_present("help-short") {
let _ = ::app::app_short().print_help();
println!("");
process::exit(0);
}
if matches.is_present("help") {
let _ = ::app::app_long().print_help();
println!("");
process::exit(0);
}
if matches.is_present("version") {
println!("ripgrep {}", crate_version!());
process::exit(0);
}
let mut logb = env_logger::LogBuilder::new(); let mut logb = env_logger::LogBuilder::new();
if matches.is_present("debug") { if matches.is_present("debug") {
@@ -156,6 +142,7 @@ impl Args {
.heading(self.heading) .heading(self.heading)
.line_per_match(self.line_per_match) .line_per_match(self.line_per_match)
.null(self.null) .null(self.null)
.only_matching(self.only_matching)
.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); .max_columns(self.max_columns);
@@ -360,6 +347,7 @@ impl<'a> ArgMatches<'a> {
no_ignore_vcs: self.no_ignore_vcs(), no_ignore_vcs: self.no_ignore_vcs(),
no_messages: self.is_present("no-messages"), no_messages: self.is_present("no-messages"),
null: self.is_present("null"), null: self.is_present("null"),
only_matching: self.is_present("only-matching"),
path_separator: try!(self.path_separator()), path_separator: try!(self.path_separator()),
quiet: quiet, quiet: quiet,
quiet_matched: QuietMatched::new(quiet), quiet_matched: QuietMatched::new(quiet),
@@ -749,7 +737,7 @@ impl<'a> ArgMatches<'a> {
if label == "auto" { if label == "auto" {
return Ok(None); return Ok(None);
} }
match Encoding::for_label(label.as_bytes()) { match Encoding::for_label_no_replacement(label.as_bytes()) {
Some(enc) => Ok(Some(enc)), Some(enc) => Ok(Some(enc)),
None => Err(From::from( None => Err(From::from(
format!("unsupported encoding: {}", label))), format!("unsupported encoding: {}", label))),
@@ -783,12 +771,18 @@ impl<'a> ArgMatches<'a> {
let casei = let casei =
self.is_present("ignore-case") self.is_present("ignore-case")
&& !self.is_present("case-sensitive"); && !self.is_present("case-sensitive");
GrepBuilder::new(&try!(self.pattern())) let mut gb = GrepBuilder::new(&try!(self.pattern()))
.case_smart(smart) .case_smart(smart)
.case_insensitive(casei) .case_insensitive(casei)
.line_terminator(b'\n') .line_terminator(b'\n');
.build()
.map_err(From::from) if let Some(limit) = try!(self.dfa_size_limit()) {
gb = gb.dfa_size_limit(limit);
}
if let Some(limit) = try!(self.regex_size_limit()) {
gb = gb.size_limit(limit);
}
gb.build().map_err(From::from)
} }
/// Builds the set of glob overrides from the command line flags. /// Builds the set of glob overrides from the command line flags.
@@ -819,31 +813,64 @@ impl<'a> ArgMatches<'a> {
btypes.build().map_err(From::from) btypes.build().map_err(From::from)
} }
/// Parses the max-filesize argument option into a byte count. /// Parses an argument of the form `[0-9]+(KMG)?`.
fn max_filesize(&self) -> Result<Option<u64>> { ///
use regex::Regex; /// This always returns the result as a type `u64`. This must be converted
/// to the appropriate type by the caller.
let max_filesize = match self.value_of_lossy("max-filesize") { fn parse_human_readable_size_arg(
&self,
arg_name: &str,
) -> Result<Option<u64>> {
let arg_value = match self.value_of_lossy(arg_name) {
Some(x) => x, Some(x) => x,
None => return Ok(None) None => return Ok(None)
}; };
let re = regex::Regex::new("^([0-9]+)([KMG])?$").unwrap();
let caps = try!(
re.captures(&arg_value).ok_or_else(|| {
format!("invalid format for {}", arg_name)
}));
let re = Regex::new("^([0-9]+)([KMG])?$").unwrap(); let value = try!(caps[1].parse::<u64>());
let caps = try!(re.captures(&max_filesize)
.ok_or("invalid format for max-filesize argument"));
let value = try!(caps[1].parse::<u64>().map_err(|err| err.to_string()));
let suffix = caps.get(2).map(|x| x.as_str()); let suffix = caps.get(2).map(|x| x.as_str());
let v_10 = value.checked_mul(1024);
let v_20 = v_10.and_then(|x| x.checked_mul(1024));
let v_30 = v_20.and_then(|x| x.checked_mul(1024));
let try_suffix = |x: Option<u64>| {
if x.is_some() {
Ok(x)
} else {
Err(From::from(format!("number too large for {}", arg_name)))
}
};
match suffix { match suffix {
None => Ok(Some(value)), None => Ok(Some(value)),
Some("K") => Ok(Some(value * 1024)), Some("K") => try_suffix(v_10),
Some("M") => Ok(Some(value * 1024 * 1024)), Some("M") => try_suffix(v_20),
Some("G") => Ok(Some(value * 1024 * 1024 * 1024)), Some("G") => try_suffix(v_30),
_ => Err(From::from("invalid suffix for max-filesize argument")) _ => Err(From::from(format!("invalid suffix for {}", arg_name)))
} }
} }
/// Parse the dfa-size-limit argument option into a byte count.
fn dfa_size_limit(&self) -> Result<Option<usize>> {
let r = try!(self.parse_human_readable_size_arg("dfa-size-limit"));
human_readable_to_usize("dfa-size-limit", r)
}
/// Parse the regex-size-limit argument option into a byte count.
fn regex_size_limit(&self) -> Result<Option<usize>> {
let r = try!(self.parse_human_readable_size_arg("regex-size-limit"));
human_readable_to_usize("regex-size-limit", r)
}
/// Parses the max-filesize argument option into a byte count.
fn max_filesize(&self) -> Result<Option<u64>> {
self.parse_human_readable_size_arg("max-filesize")
}
/// Returns true if ignore files should be ignored. /// Returns true if ignore files should be ignored.
fn no_ignore(&self) -> bool { fn no_ignore(&self) -> bool {
self.is_present("no-ignore") self.is_present("no-ignore")
@@ -938,6 +965,27 @@ impl QuietMatched {
} }
} }
/// Convert the result of a `parse_human_readable_size_arg` call into
/// a `usize`, failing if the type does not fit.
fn human_readable_to_usize(
arg_name: &str,
value: Option<u64>,
) -> Result<Option<usize>> {
use std::usize;
match value {
None => Ok(None),
Some(v) => {
if v <= usize::MAX as u64 {
Ok(Some(v as usize))
} else {
let msg = format!("number too large for {}", arg_name);
Err(From::from(msg))
}
}
}
}
/// Returns true if and only if stdin is deemed searchable. /// Returns true if and only if stdin is deemed searchable.
#[cfg(unix)] #[cfg(unix)]
fn stdin_is_readable() -> bool { fn stdin_is_readable() -> bool {

View File

@@ -1,5 +1,3 @@
#![allow(dead_code)]
use std::cmp; use std::cmp;
use std::io::{self, Read}; use std::io::{self, Read};
@@ -290,10 +288,6 @@ mod tests {
use super::{Bom, BomPeeker, DecodeReader}; 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 { fn read_to_string<R: Read>(mut rdr: R) -> String {
let mut s = String::new(); let mut s = String::new();
rdr.read_to_string(&mut s).unwrap(); rdr.read_to_string(&mut s).unwrap();
@@ -453,7 +447,8 @@ mod tests {
test_trans_simple!(trans_simple_utf16be, "utf-16be", b"\x04\x16", "Ж"); 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_chinese, "chinese", b"\xA7\xA8", "Ж");
test_trans_simple!(trans_simple_korean, "korean", b"\xAC\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_big5_hkscs, "big5-hkscs", b"\xC7\xFA", "Ж");
test_trans_simple!(trans_simple_gbk, "gbk", b"\xA7\xA8", "Ж"); 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_sjis, "sjis", b"\x84\x47", "Ж");
test_trans_simple!(trans_simple_eucjp, "euc-jp", b"\xA7\xA8", "Ж"); test_trans_simple!(trans_simple_eucjp, "euc-jp", b"\xA7\xA8", "Ж");

View File

@@ -58,6 +58,8 @@ pub struct Printer<W> {
/// Whether to print NUL bytes after a file path instead of new lines /// Whether to print NUL bytes after a file path instead of new lines
/// or `:`. /// or `:`.
null: bool, null: bool,
/// Print only the matched (non-empty) parts of a matching line
only_matching: bool,
/// A string to use as a replacement of each match in a matching line. /// A string to use as a replacement of each match in a matching line.
replace: Option<Vec<u8>>, replace: Option<Vec<u8>>,
/// Whether to prefix each match with the corresponding file name. /// Whether to prefix each match with the corresponding file name.
@@ -83,6 +85,7 @@ impl<W: WriteColor> Printer<W> {
heading: false, heading: false,
line_per_match: false, line_per_match: false,
null: false, null: false,
only_matching: false,
replace: None, replace: None,
with_filename: false, with_filename: false,
colors: ColorSpecs::default(), colors: ColorSpecs::default(),
@@ -144,6 +147,12 @@ impl<W: WriteColor> Printer<W> {
self self
} }
/// Print only the matched (non-empty) parts of a matching line
pub fn only_matching(mut self, yes: bool) -> Printer<W> {
self.only_matching = yes;
self
}
/// A separator to use when printing file paths. When empty, use the /// A separator to use when printing file paths. When empty, use the
/// default separator for the current platform. (/ on Unix, \ on Windows.) /// default separator for the current platform. (/ on Unix, \ on Windows.)
pub fn path_separator(mut self, sep: Option<u8>) -> Printer<W> { pub fn path_separator(mut self, sep: Option<u8>) -> Printer<W> {
@@ -153,9 +162,6 @@ impl<W: WriteColor> Printer<W> {
/// Replace every match in each matching line with the replacement string /// Replace every match in each matching line with the replacement string
/// given. /// given.
///
/// The replacement string syntax is documented here:
/// https://doc.rust-lang.org/regex/regex/bytes/struct.Captures.html#method.expand
pub fn replace(mut self, replacement: Vec<u8>) -> Printer<W> { pub fn replace(mut self, replacement: Vec<u8>) -> Printer<W> {
self.replace = Some(replacement); self.replace = Some(replacement);
self self
@@ -204,22 +210,14 @@ impl<W: WriteColor> Printer<W> {
pub fn path<P: AsRef<Path>>(&mut self, path: P) { pub fn path<P: AsRef<Path>>(&mut self, path: P) {
let path = strip_prefix("./", path.as_ref()).unwrap_or(path.as_ref()); let path = strip_prefix("./", path.as_ref()).unwrap_or(path.as_ref());
self.write_path(path); self.write_path(path);
if self.null { self.write_path_eol();
self.write(b"\x00");
} else {
self.write_eol();
}
} }
/// Prints the given path and a count of the number of matches found. /// Prints the given path and a count of the number of matches found.
pub fn path_count<P: AsRef<Path>>(&mut self, path: P, count: u64) { pub fn path_count<P: AsRef<Path>>(&mut self, path: P, count: u64) {
if self.with_filename { if self.with_filename {
self.write_path(path); self.write_path(path);
if self.null { self.write_path_sep(b':');
self.write(b"\x00");
} else {
self.write(b":");
}
} }
self.write(count.to_string().as_bytes()); self.write(count.to_string().as_bytes());
self.write_eol(); self.write_eol();
@@ -227,13 +225,11 @@ impl<W: WriteColor> Printer<W> {
/// Prints the context separator. /// Prints the context separator.
pub fn context_separate(&mut self) { pub fn context_separate(&mut self) {
// N.B. We can't use `write` here because of borrowing restrictions.
if self.context_separator.is_empty() { if self.context_separator.is_empty() {
return; return;
} }
self.has_printed = true;
let _ = self.wtr.write_all(&self.context_separator); let _ = self.wtr.write_all(&self.context_separator);
let _ = self.wtr.write_all(&[self.eol]); self.write_eol();
} }
pub fn matched<P: AsRef<Path>>( pub fn matched<P: AsRef<Path>>(
@@ -245,24 +241,14 @@ impl<W: WriteColor> Printer<W> {
end: usize, end: usize,
line_number: Option<u64>, line_number: Option<u64>,
) { ) {
if !self.line_per_match { if !self.line_per_match && !self.only_matching {
let column = let column = re.find(&buf[start..end])
if self.column { .map(|m| m.start()).unwrap_or(0);
Some(re.find(&buf[start..end])
.map(|m| m.start()).unwrap_or(0) as u64)
} else {
None
};
return self.write_match( return self.write_match(
re, path, buf, start, end, line_number, column); re, path, buf, start, end, line_number, column);
} }
for m in re.find_iter(&buf[start..end]) { for m in re.find_iter(&buf[start..end]) {
let column = let column = m.start();
if self.column {
Some(m.start() as u64)
} else {
None
};
self.write_match( self.write_match(
re, path.as_ref(), buf, start, end, line_number, column); re, path.as_ref(), buf, start, end, line_number, column);
} }
@@ -276,20 +262,21 @@ impl<W: WriteColor> Printer<W> {
start: usize, start: usize,
end: usize, end: usize,
line_number: Option<u64>, line_number: Option<u64>,
column: Option<u64>, column: usize,
) { ) {
if self.heading && self.with_filename && !self.has_printed { if self.heading && self.with_filename && !self.has_printed {
self.write_file_sep(); self.write_file_sep();
self.write_heading(path.as_ref()); self.write_path(path);
self.write_path_eol();
} else if !self.heading && self.with_filename { } else if !self.heading && self.with_filename {
self.write_non_heading_path(path.as_ref()); self.write_path(path);
self.write_path_sep(b':');
} }
if let Some(line_number) = line_number { if let Some(line_number) = line_number {
self.line_number(line_number, b':'); self.line_number(line_number, b':');
} }
if let Some(c) = column { if self.column {
self.write((c + 1).to_string().as_bytes()); self.column_number(column as u64 + 1, b':');
self.write(b":");
} }
if self.replace.is_some() { if self.replace.is_some() {
let mut count = 0; let mut count = 0;
@@ -299,11 +286,9 @@ impl<W: WriteColor> Printer<W> {
re.replace_all(&buf[start..end], replacer) re.replace_all(&buf[start..end], replacer)
}; };
if self.max_columns.map_or(false, |m| line.len() > m) { if self.max_columns.map_or(false, |m| line.len() > m) {
let _ = self.wtr.set_color(self.colors.matched());
let msg = format!( let msg = format!(
"[Omitted long line with {} replacements]", count); "[Omitted long line with {} replacements]", count);
self.write(msg.as_bytes()); self.write_colored(msg.as_bytes(), |colors| colors.matched());
let _ = self.wtr.reset();
self.write_eol(); self.write_eol();
return; return;
} }
@@ -312,7 +297,14 @@ impl<W: WriteColor> Printer<W> {
self.write_eol(); self.write_eol();
} }
} else { } else {
self.write_matched_line(re, &buf[start..end]); let line_buf = if self.only_matching {
let start_offset = start + column;
let m = re.find(&buf[start_offset..end]).unwrap();
&buf[start_offset + m.start()..start_offset + m.end()]
} else {
&buf[start..end]
};
self.write_matched_line(re, line_buf);
// write_matched_line guarantees to write a newline. // write_matched_line guarantees to write a newline.
} }
} }
@@ -320,10 +312,8 @@ impl<W: WriteColor> Printer<W> {
fn write_matched_line(&mut self, re: &Regex, buf: &[u8]) { fn write_matched_line(&mut self, re: &Regex, buf: &[u8]) {
if self.max_columns.map_or(false, |m| buf.len() > m) { if self.max_columns.map_or(false, |m| buf.len() > m) {
let count = re.find_iter(buf).count(); let count = re.find_iter(buf).count();
let _ = self.wtr.set_color(self.colors.matched());
let msg = format!("[Omitted long line with {} matches]", count); let msg = format!("[Omitted long line with {} matches]", count);
self.write(msg.as_bytes()); self.write_colored(msg.as_bytes(), |colors| colors.matched());
let _ = self.wtr.reset();
self.write_eol(); self.write_eol();
return; return;
} }
@@ -333,9 +323,8 @@ impl<W: WriteColor> Printer<W> {
let mut last_written = 0; let mut last_written = 0;
for m in re.find_iter(buf) { for m in re.find_iter(buf) {
self.write(&buf[last_written..m.start()]); self.write(&buf[last_written..m.start()]);
let _ = self.wtr.set_color(self.colors.matched()); self.write_colored(
self.write(&buf[m.start()..m.end()]); &buf[m.start()..m.end()], |colors| colors.matched());
let _ = self.wtr.reset();
last_written = m.end(); last_written = m.end();
} }
self.write(&buf[last_written..]); self.write(&buf[last_written..]);
@@ -355,14 +344,11 @@ impl<W: WriteColor> Printer<W> {
) { ) {
if self.heading && self.with_filename && !self.has_printed { if self.heading && self.with_filename && !self.has_printed {
self.write_file_sep(); self.write_file_sep();
self.write_heading(path.as_ref()); self.write_path(path);
self.write_path_eol();
} else if !self.heading && self.with_filename { } else if !self.heading && self.with_filename {
self.write_path(path.as_ref()); self.write_path(path);
if self.null { self.write_path_sep(b'-');
self.write(b"\x00");
} else {
self.write(b"-");
}
} }
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'-');
@@ -378,10 +364,19 @@ impl<W: WriteColor> Printer<W> {
} }
} }
fn write_heading<P: AsRef<Path>>(&mut self, path: P) { fn separator(&mut self, sep: &[u8]) {
let _ = self.wtr.set_color(self.colors.path()); self.write(&sep);
self.write_path(path.as_ref()); }
let _ = self.wtr.reset();
fn write_path_sep(&mut self, sep: u8) {
if self.null {
self.write(b"\x00");
} else {
self.separator(&[sep]);
}
}
fn write_path_eol(&mut self) {
if self.null { if self.null {
self.write(b"\x00"); self.write(b"\x00");
} else { } else {
@@ -389,52 +384,43 @@ impl<W: WriteColor> Printer<W> {
} }
} }
fn write_non_heading_path<P: AsRef<Path>>(&mut self, path: P) {
let _ = self.wtr.set_color(self.colors.path());
self.write_path(path.as_ref());
let _ = self.wtr.reset();
if self.null {
self.write(b"\x00");
} else {
self.write(b":");
}
}
fn line_number(&mut self, n: u64, sep: u8) {
let _ = self.wtr.set_color(self.colors.line());
self.write(n.to_string().as_bytes());
let _ = self.wtr.reset();
self.write(&[sep]);
}
#[cfg(unix)] #[cfg(unix)]
fn write_path<P: AsRef<Path>>(&mut self, path: P) { fn write_path<P: AsRef<Path>>(&mut self, path: P) {
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
let path = path.as_ref().as_os_str().as_bytes(); let path = path.as_ref().as_os_str().as_bytes();
match self.path_separator { self.write_path_replace_separator(path);
None => self.write(path),
Some(sep) => self.write_path_with_sep(path, sep),
}
} }
#[cfg(not(unix))] #[cfg(not(unix))]
fn write_path<P: AsRef<Path>>(&mut self, path: P) { fn write_path<P: AsRef<Path>>(&mut self, path: P) {
let path = path.as_ref().to_string_lossy(); let path = path.as_ref().to_string_lossy();
self.write_path_replace_separator(path.as_bytes());
}
fn write_path_replace_separator(&mut self, path: &[u8]) {
match self.path_separator { match self.path_separator {
None => self.write(path.as_bytes()), None => self.write_colored(path, |colors| colors.path()),
Some(sep) => self.write_path_with_sep(path.as_bytes(), sep), Some(sep) => {
let transformed_path: Vec<_> = path.iter().map(|&b| {
if b == b'/' || (cfg!(windows) && b == b'\\') {
sep
} else {
b
}
}).collect();
self.write_colored(&transformed_path, |colors| colors.path());
}
} }
} }
fn write_path_with_sep(&mut self, path: &[u8], sep: u8) { fn line_number(&mut self, n: u64, sep: u8) {
let mut path = path.to_vec(); self.write_colored(n.to_string().as_bytes(), |colors| colors.line());
for b in &mut path { self.separator(&[sep]);
if *b == b'/' || (cfg!(windows) && *b == b'\\') {
*b = sep;
} }
}
self.write(&path); fn column_number(&mut self, n: u64, sep: u8) {
self.write_colored(n.to_string().as_bytes(), |colors| colors.column());
self.separator(&[sep]);
} }
fn write(&mut self, buf: &[u8]) { fn write(&mut self, buf: &[u8]) {
@@ -447,6 +433,14 @@ impl<W: WriteColor> Printer<W> {
self.write(&[eol]); self.write(&[eol]);
} }
fn write_colored<F>(&mut self, buf: &[u8], get_color: F)
where F: Fn(&ColorSpecs) -> &ColorSpec
{
let _ = self.wtr.set_color( get_color(&self.colors) );
self.write(buf);
let _ = self.wtr.reset();
}
fn write_file_sep(&mut self) { fn write_file_sep(&mut self) {
if let Some(ref sep) = self.file_separator { if let Some(ref sep) = self.file_separator {
self.has_printed = true; self.has_printed = true;
@@ -492,7 +486,7 @@ impl fmt::Display for Error {
match *self { match *self {
Error::UnrecognizedOutType(ref name) => { Error::UnrecognizedOutType(ref name) => {
write!(f, "Unrecognized output type '{}'. Choose from: \ write!(f, "Unrecognized output type '{}'. Choose from: \
path, line, match.", name) path, line, column, match.", name)
} }
Error::UnrecognizedSpecType(ref name) => { Error::UnrecognizedSpecType(ref name) => {
write!(f, "Unrecognized spec type '{}'. Choose from: \ write!(f, "Unrecognized spec type '{}'. Choose from: \
@@ -503,11 +497,13 @@ impl fmt::Display for Error {
} }
Error::UnrecognizedStyle(ref name) => { Error::UnrecognizedStyle(ref name) => {
write!(f, "Unrecognized style attribute '{}'. Choose from: \ write!(f, "Unrecognized style attribute '{}'. Choose from: \
nobold, bold.", name) nobold, bold, nointense, intense.", name)
} }
Error::InvalidFormat(ref original) => { Error::InvalidFormat(ref original) => {
write!(f, "Invalid color speci format: '{}'. Valid format \ write!(
is '(path|line|match):(fg|bg|style):(value)'.", f,
"Invalid color speci format: '{}'. Valid format \
is '(path|line|column|match):(fg|bg|style):(value)'.",
original) original)
} }
} }
@@ -525,6 +521,7 @@ impl From<ParseColorError> for Error {
pub struct ColorSpecs { pub struct ColorSpecs {
path: ColorSpec, path: ColorSpec,
line: ColorSpec, line: ColorSpec,
column: ColorSpec,
matched: ColorSpec, matched: ColorSpec,
} }
@@ -554,7 +551,7 @@ pub struct ColorSpecs {
/// The format of a `Spec` is a triple: `{type}:{attribute}:{value}`. Each /// The format of a `Spec` is a triple: `{type}:{attribute}:{value}`. Each
/// component is defined as follows: /// component is defined as follows:
/// ///
/// * `{type}` can be one of `path`, `line` or `match`. /// * `{type}` can be one of `path`, `line`, `column` or `match`.
/// * `{attribute}` can be one of `fg`, `bg` or `style`. `{attribute}` may also /// * `{attribute}` can be one of `fg`, `bg` or `style`. `{attribute}` may also
/// be the special value `none`, in which case, `{value}` can be omitted. /// be the special value `none`, in which case, `{value}` can be omitted.
/// * `{value}` is either a color name (for `fg`/`bg`) or a style instruction. /// * `{value}` is either a color name (for `fg`/`bg`) or a style instruction.
@@ -593,6 +590,7 @@ enum SpecValue {
enum OutType { enum OutType {
Path, Path,
Line, Line,
Column,
Match, Match,
} }
@@ -623,6 +621,7 @@ impl ColorSpecs {
match user_spec.ty { match user_spec.ty {
OutType::Path => user_spec.merge_into(&mut specs.path), OutType::Path => user_spec.merge_into(&mut specs.path),
OutType::Line => user_spec.merge_into(&mut specs.line), OutType::Line => user_spec.merge_into(&mut specs.line),
OutType::Column => user_spec.merge_into(&mut specs.column),
OutType::Match => user_spec.merge_into(&mut specs.matched), OutType::Match => user_spec.merge_into(&mut specs.matched),
} }
} }
@@ -639,6 +638,11 @@ impl ColorSpecs {
&self.line &self.line
} }
/// Return the color specification for coloring column numbers.
fn column(&self) -> &ColorSpec {
&self.column
}
/// Return the color specification for coloring matched text. /// Return the color specification for coloring matched text.
fn matched(&self) -> &ColorSpec { fn matched(&self) -> &ColorSpec {
&self.matched &self.matched
@@ -714,6 +718,7 @@ impl FromStr for OutType {
match &*s.to_lowercase() { match &*s.to_lowercase() {
"path" => Ok(OutType::Path), "path" => Ok(OutType::Path),
"line" => Ok(OutType::Line), "line" => Ok(OutType::Line),
"column" => Ok(OutType::Column),
"match" => Ok(OutType::Match), "match" => Ok(OutType::Match),
_ => Err(Error::UnrecognizedOutType(s.to_string())), _ => Err(Error::UnrecognizedOutType(s.to_string())),
} }
@@ -765,6 +770,7 @@ mod tests {
assert_eq!(ColorSpecs::new(user_specs), ColorSpecs { assert_eq!(ColorSpecs::new(user_specs), ColorSpecs {
path: ColorSpec::default(), path: ColorSpec::default(),
line: ColorSpec::default(), line: ColorSpec::default(),
column: ColorSpec::default(),
matched: expect_matched, matched: expect_matched,
}); });
} }
@@ -800,6 +806,12 @@ mod tests {
ty: OutType::Line, ty: OutType::Line,
value: SpecValue::None, value: SpecValue::None,
}); });
let spec: Spec = "column:bg:green".parse().unwrap();
assert_eq!(spec, Spec {
ty: OutType::Column,
value: SpecValue::Bg(Color::Green),
});
} }
#[test] #[test]

View File

@@ -3,8 +3,8 @@ The `search_buffer` module is responsible for searching a single file all in a
single buffer. Typically, the source of the buffer is a memory map. This can single buffer. Typically, the source of the buffer is a memory map. This can
be useful for when memory maps are faster than streaming search. be useful for when memory maps are faster than streaming search.
Note that this module doesn't quite support everything that `search_stream` does. Note that this module doesn't quite support everything that `search_stream`
Notably, showing contexts. does. Notably, showing contexts.
*/ */
use std::cmp; use std::cmp;
use std::path::Path; use std::path::Path;

3
termcolor/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 = "termcolor" name = "termcolor"
version = "0.3.1" #:version version = "0.3.2" #: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.

21
termcolor/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
termcolor/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

@@ -455,7 +455,6 @@ sherlock!(max_filesize_parse_no_suffix, "Sherlock", ".",
let expected = "\ let expected = "\
foo foo
"; ";
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
@@ -470,7 +469,6 @@ sherlock!(max_filesize_parse_k_suffix, "Sherlock", ".",
let expected = "\ let expected = "\
foo foo
"; ";
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
@@ -485,10 +483,19 @@ sherlock!(max_filesize_parse_m_suffix, "Sherlock", ".",
let expected = "\ let expected = "\
foo foo
"; ";
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
sherlock!(max_filesize_suffix_overflow, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
wd.remove("sherlock");
wd.create_size("foo", 1000000);
// 2^35 * 2^30 would otherwise overflow
cmd.arg("--max-filesize").arg("34359738368G").arg("--files");
wd.assert_err(&mut cmd);
});
sherlock!(ignore_hidden, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| { sherlock!(ignore_hidden, "Sherlock", ".", |wd: WorkDir, mut cmd: Command| {
wd.remove("sherlock"); wd.remove("sherlock");
wd.create(".sherlock", hay::SHERLOCK); wd.create(".sherlock", hay::SHERLOCK);
@@ -1057,6 +1064,33 @@ clean!(regression_405, "test", ".", |wd: WorkDir, mut cmd: Command| {
assert_eq!(lines, format!("{}:test\n", path("bar/foo/file2.txt"))); assert_eq!(lines, format!("{}:test\n", path("bar/foo/file2.txt")));
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/428
clean!(regression_428_color_context_path, "foo", ".", |wd: WorkDir, mut cmd: Command| {
wd.create("sherlock", "foo\nbar");
cmd.arg("-A1").arg("-H").arg("--no-heading").arg("-N")
.arg("--colors=match:none").arg("--color=always");
let lines: String = wd.stdout(&mut cmd);
let expected = format!("\
{colored_path}:foo
{colored_path}-bar
", colored_path=format!("\x1b\x5b\x6d\x1b\x5b\x33\x35\x6d{path}\x1b\x5b\x6d", path=path("sherlock")));
assert_eq!(lines, expected);
});
// See: https://github.com/BurntSushi/ripgrep/issues/428
clean!(regression_428_unrecognized_style, "Sherlok", ".", |wd: WorkDir, mut cmd: Command| {
cmd.arg("--colors=match:style:");
wd.assert_err(&mut cmd);
let output = cmd.output().unwrap();
let err = String::from_utf8_lossy(&output.stderr);
let expected = "\
Unrecognized style attribute ''. Choose from: nobold, bold, nointense, intense.
";
assert_eq!(err, expected);
});
// See: https://github.com/BurntSushi/ripgrep/issues/1 // See: https://github.com/BurntSushi/ripgrep/issues/1
clean!(feature_1_sjis, "Шерлок Холмс", ".", |wd: WorkDir, mut cmd: Command| { clean!(feature_1_sjis, "Шерлок Холмс", ".", |wd: WorkDir, mut cmd: Command| {
let sherlock = let sherlock =
@@ -1103,6 +1137,21 @@ clean!(feature_1_eucjp, "Шерлок Холмс", ".",
assert_eq!(lines, "foo:Шерлок Холмс\n"); assert_eq!(lines, "foo:Шерлок Холмс\n");
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/1
sherlock!(feature_1_unknown_encoding, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("-Efoobar");
wd.assert_non_empty_stderr(&mut cmd);
});
// See: https://github.com/BurntSushi/ripgrep/issues/1
// Specific: https://github.com/BurntSushi/ripgrep/pull/398/files#r111109265
sherlock!(feature_1_replacement_encoding, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("-Ecsiso2022kr");
wd.assert_non_empty_stderr(&mut cmd);
});
// See: https://github.com/BurntSushi/ripgrep/issues/7 // See: https://github.com/BurntSushi/ripgrep/issues/7
sherlock!(feature_7, "-fpat", "sherlock", |wd: WorkDir, mut cmd: Command| { sherlock!(feature_7, "-fpat", "sherlock", |wd: WorkDir, mut cmd: Command| {
wd.create("pat", "Sherlock\nHolmes"); wd.create("pat", "Sherlock\nHolmes");
@@ -1139,6 +1188,32 @@ be, to a very large extent, the result of luck. Sherlock Holmes
assert_eq!(lines, expected); assert_eq!(lines, expected);
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/34
sherlock!(feature_34_only_matching, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--only-matching");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
sherlock:Sherlock
sherlock:Sherlock
";
assert_eq!(lines, expected);
});
// See: https://github.com/BurntSushi/ripgrep/issues/34
sherlock!(feature_34_only_matching_line_column, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--only-matching").arg("--column").arg("--line-number");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
sherlock:1:57:Sherlock
sherlock:3:49:Sherlock
";
assert_eq!(lines, expected);
});
// See: https://github.com/BurntSushi/ripgrep/issues/45 // See: https://github.com/BurntSushi/ripgrep/issues/45
sherlock!(feature_45_relative_cwd, "test", ".", sherlock!(feature_45_relative_cwd, "test", ".",
|wd: WorkDir, mut cmd: Command| { |wd: WorkDir, mut cmd: Command| {
@@ -1390,6 +1465,46 @@ clean!(feature_275_pathsep, "test", ".", |wd: WorkDir, mut cmd: Command| {
assert_eq!(lines, "fooZbar:test\n"); assert_eq!(lines, "fooZbar:test\n");
}); });
// See: https://github.com/BurntSushi/ripgrep/issues/362
sherlock!(feature_362_dfa_size_limit, r"For\s",
|wd: WorkDir, mut cmd: Command| {
// This should fall back to the nfa engine but should still produce the
// expected result.
cmd.arg("--dfa-size-limit").arg("10");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
For the Doctor Watsons of this world, as opposed to the Sherlock
";
assert_eq!(lines, expected);
});
sherlock!(feature_362_exceeds_regex_size_limit, r"[0-9]\w+",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("--regex-size-limit").arg("10K");
wd.assert_err(&mut cmd);
});
#[cfg(target_pointer_width = "32")]
sherlock!(feature_362_u64_to_narrow_usize_suffix_overflow, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
wd.remove("sherlock");
wd.create_size("foo", 1000000);
// 2^35 * 2^20 is ok for u64, but not for usize
cmd.arg("--dfa-size-limit").arg("34359738368M").arg("--files");
wd.assert_err(&mut cmd);
});
// See: https://github.com/BurntSushi/ripgrep/issues/419
sherlock!(feature_419_zero_as_shortcut_for_null, "Sherlock", ".",
|wd: WorkDir, mut cmd: Command| {
cmd.arg("-0").arg("--count");
let lines: String = wd.stdout(&mut cmd);
assert_eq!(lines, "sherlock\x002\n");
});
#[test] #[test]
fn binary_nosearch() { fn binary_nosearch() {
let wd = WorkDir::new("binary_nosearch"); let wd = WorkDir::new("binary_nosearch");
@@ -1492,6 +1607,51 @@ fn regression_391() {
assert_eq!(lines, "bar.py\n"); assert_eq!(lines, "bar.py\n");
} }
// See: https://github.com/BurntSushi/ripgrep/issues/451
#[test]
fn regression_451_only_matching_as_in_issue() {
let wd = WorkDir::new("regression_451_only_matching");
let path = "digits.txt";
wd.create(path, "1 2 3\n");
let mut cmd = wd.command();
cmd.arg("[0-9]+").arg(path).arg("--only-matching");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
1
2
3
";
assert_eq!(lines, expected);
}
// See: https://github.com/BurntSushi/ripgrep/issues/451
#[test]
fn regression_451_only_matching() {
let wd = WorkDir::new("regression_451_only_matching");
let path = "digits.txt";
wd.create(path, "1 2 3\n123\n");
let mut cmd = wd.command();
cmd.arg("[0-9]").arg(path)
.arg("--only-matching")
.arg("--column");
let lines: String = wd.stdout(&mut cmd);
let expected = "\
1:1:1
1:3:2
1:5:3
2:1:1
2:2:2
2:3:3
";
assert_eq!(lines, expected);
}
#[test] #[test]
fn type_list() { fn type_list() {
let wd = WorkDir::new("type_list"); let wd = WorkDir::new("type_list");

View File

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