Skip to content

Commit

Permalink
Merge branch 'unrestricted'
Browse files Browse the repository at this point in the history
  • Loading branch information
rhysd committed Apr 3, 2024
2 parents 01b580a + e9158fc commit bd624a5
Show file tree
Hide file tree
Showing 47 changed files with 998 additions and 22 deletions.
31 changes: 24 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,23 @@ be specified by `-C`. If you don't want the heuristics, give the same value to t

```sh
# At least 10 context lines and at most 20 context lines
hgrep pattern -c 10 -C 20 paths...
hgrep -c 10 -C 20 pattern paths...
```

Compared to receiveing inputs from `rg` via pipe, it's fast to handle so many matches in the same process. In combination with
`syntect-printer` feature, matched regions can be highlighted in a searched text color. The built-in grep feature is enabled by
default and can be omitted by feature flags.
Like ripgrep, the built-in grep filters files by default. It looks at ignore files such as `.gitignore` and ignores hidden files.
To disable the filters, `--no-ignore` and `--hidden` are available respectively. `-u` is a useful shortcut for them.

```sh
# Same as `hgrep --no-ignore pattern paths...`
hgrep -u pattern paths...

# Same as `hgrep --no-ignore --hidden pattern paths...`
hgrep -uu pattern paths...
```

Regarding to the performance compared to receiveing inputs from `grep` or `rg` via pipe, it's fast to handle so many matches in
the same process. In combination with `syntect-printer` feature, matched regions can be highlighted in a searched text color.
The built-in grep feature is enabled by default and can be omitted by feature flags.

Though almost all useful options are implemented, the built-in grep implementation is a subset of ripgrep. If you need full
functionalities, use `rg` command and eat its output by hgrep via stdin. Currently there are the following restrictions.
Expand All @@ -147,6 +158,7 @@ functionalities, use `rg` command and eat its output by hgrep via stdin. Current
- Memory map is not used until `--mmap` flag is specified
- Adding and removing file types are not supported. Only default file types are supported (see `--type-list`)
- `.ripgreprc` config file is not supported
- Searching binary files (`--binary`) is not supported

### Eating `grep -nH` output

Expand Down Expand Up @@ -274,9 +286,11 @@ Here are some examples:
```sh
# Set the `ayu-dark` color theme with background colors
export HGREP_DEFAULT_OPTS='--theme ayu-dark --background'
# Same as `hgrep --theme ayu-dark --background pattern paths`
hgrep pattern paths...

# Search hidden files
export HGREP_DEFAULT_OPTS='--hidden'
# Disable automatic filtering
export HGREP_DEFAULT_OPTS='--no-ignore --hidden'

# Use 'bat' printer by default
export HGREP_DEFAULT_OPTS='--printer bat'
Expand All @@ -294,7 +308,7 @@ $Env:HGREP_DEFAULT_OPTS = "--glob '!C:\Program Files'"
- Common options
- `--min-context NUM` (`-c`): Minimum lines of leading and trailing context surrounding each match. Default value is 3
- `--max-context NUM` (`-C`): Maximum lines of leading and trailing context surrounding each match. Default value is 6
- `--no-grid` (`-G`): Remove borderlines for more compact output. `--grid` flag is an opposite of this flag
- `--no-grid` (`-G`): Remove borderlines for more compact output. --grid flag is an opposite of this flag
- `--tab NUM`: Number of spaces for tab character. Set 0 to pass tabs through. Default value is 4
- `--theme THEME`: Theme for syntax highlighting. Default value is the same as `bat` command
- `--list-themes`: List all available theme names and their samples for --theme option
Expand All @@ -307,6 +321,9 @@ $Env:HGREP_DEFAULT_OPTS = "--glob '!C:\Program Files'"
- `--ignore-case` (`-i`): When this flag is provided, the given pattern will be searched case insensitively
- `--smart-case` (`-S`): Search case insensitively if the pattern is all lowercase. Search case sensitively otherwise
- `--hidden` (`-.`): Search hidden files and directories. By default, hidden files and directories are skipped
- `--unrestricted` (`-u`): Reduce the level of "smart" filtering by repeated uses (up to 2). A single flag `-u` is equivalent to --no-ignore.
Two flags `-uu` are equivalent to --no-ignore --hidden. Unlike ripgrep, three flags `-uuu` are not supported since hgrep doesn't support
--binary flag
- `--glob GLOB...` (`-g`): Include or exclude files and directories for searching that match the given glob
- `--glob-case-insensitive`: Process glob patterns given with the -g/--glob flag case insensitively
- `--fixed-strings` (`-F`): Treat the pattern as a literal string instead of a regular expression
Expand Down
104 changes: 89 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,13 @@ fn command() -> Command {
.value_name("NUM+SUFFIX?")
.help("The upper size limit of the regex DFA. The default limit is 10M. For the size suffixes, see --max-filesize"),
)
.arg(
Arg::new("unrestricted")
.short('u')
.long("unrestricted")
.action(ArgAction::Count)
.help(r#"Reduce the level of "smart" filtering by repeated uses (up to 2). A single flag is equivalent to --no-ignore. Two flags are equivalent to --no-ignore --hidden. Unlike ripgrep, three flags are not supported since hgrep doesn't support --binary flag"#)
)
.arg(
Arg::new("PATTERN")
.help("Pattern to search. Regular expression is available"),
Expand Down Expand Up @@ -527,6 +534,17 @@ fn build_ripgrep_config(
config.types_not(types_not.map(String::as_str));
}

match matches.get_count("unrestricted") {
0 => {}
1 => {
config.no_ignore(true);
}
2 => {
config.no_ignore(true).hidden(true);
}
_ => anyhow::bail!("-u or --unrestricted cannot be repeated more than twice. Try -uu to search every text file"),
}

Ok(config)
}

Expand Down Expand Up @@ -837,6 +855,9 @@ mod tests {
);
snapshot_test!(generate_man_page, ["--generate-man-page"]);
snapshot_test!(max_filesize, ["--max-filesize", "100M"]);
snapshot_test!(unrestricted_once, ["-u"]);
snapshot_test!(unrestricted_twice, ["-u", "-u"]);
snapshot_test!(unrestricted_twice_in_single_flag, ["-uu"]);
snapshot_test!(
all_printer_opts_before_args,
[
Expand Down Expand Up @@ -896,23 +917,45 @@ mod tests {
]
);

#[test]
fn invalid_option() {
for args in [
&["--min-context", "foo"][..],
&["--max-context", "foo"][..],
&["--term-width", "foo"][..],
&["--term-width", "1"][..],
&["--tab", "foo"][..],
&["--printer", "syntect", "--custom-assets"][..],
&["--printer", "bat", "--background"][..],
&["--printer", "bat", "--ascii-lines"][..],
] {
let mat = command().try_get_matches_from(args).unwrap();
assert!(run(mat).is_err(), "args: {:?}", args);
}
macro_rules! snapshot_error_test {
($name:ident, $args:expr) => {
#[test]
fn $name() {
use std::fmt::Write;
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path(SNAPSHOT_DIR);
settings.bind(|| {
let cmd = command();
let mat = cmd.try_get_matches_from($args).unwrap();
let err = run(mat).unwrap_err();
let mut msg = format!("{err}");
for err in err.chain().skip(1) {
write!(msg, " -> {err}").unwrap();
}
insta::assert_debug_snapshot!(msg);
});
}
};
}

snapshot_error_test!(invalid_min_context, ["--min-context", "foo"]);
snapshot_error_test!(invalid_max_context, ["--max-context", "foo"]);
snapshot_error_test!(invalid_term_width, ["--term-width", "foo"]);
snapshot_error_test!(term_width_too_small, ["--term-width", "1"]);
snapshot_error_test!(invalid_tab_width, ["--tab", "foo"]);
snapshot_error_test!(
invalid_opt_for_syntect,
["--printer", "syntect", "--custom-assets"]
);
snapshot_error_test!(
bat_doesnt_support_background,
["--printer", "bat", "--background"]
);
snapshot_error_test!(
bat_doesnt_support_ascii_lines,
["--printer", "bat", "--ascii-lines"]
);

#[test]
fn arg_parser_debug_assert() {
command().debug_assert();
Expand Down Expand Up @@ -1021,6 +1064,37 @@ mod tests {
["-i", "-S", "-F", "-w", "-L", "-U", "-.", "-x", "-P", "pat", "dir"]
);
snapshot_test!(max_filesize, ["--max-filesize", "100M"]);
snapshot_test!(unrestricted_once, ["-u"]);
snapshot_test!(unrestricted_twice, ["-u", "-u"]);

macro_rules! snapshot_error_test {
($name:ident, $args:expr) => {
#[test]
fn $name() {
use std::fmt::Write;
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path(SNAPSHOT_DIR);
settings.bind(|| {
let cmd = command();
let mat = cmd.try_get_matches_from($args).unwrap();
let err = build_ripgrep_config(3, 6, &mat).unwrap_err();
let mut msg = format!("{err}");
for err in err.chain().skip(1) {
write!(msg, " -> {err}").unwrap();
}
insta::assert_debug_snapshot!(msg);
});
}
};
}

snapshot_error_test!(max_count_parse_error, ["--max-count", "foo"]);
snapshot_error_test!(max_depth_parse_error, ["--max-depth", "foo"]);
snapshot_error_test!(max_filesize_parse_error, ["--max-filesize", "foo"]);
snapshot_error_test!(regex_size_limit_parse_error, ["--regex-size-limit", "foo"]);
snapshot_error_test!(dfa_size_limit_parse_error, ["--dfa-size-limit", "foo"]);
snapshot_error_test!(too_many_u_flags_mutiple, ["-u", "-u", "-u"]);
snapshot_error_test!(too_many_u_flags_single, ["-uuu"]);
}

#[test]
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions testdata/snapshots/hgrep__tests__arg_matches__background.snap

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions testdata/snapshots/hgrep__tests__arg_matches__first_only.snap

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions testdata/snapshots/hgrep__tests__arg_matches__grid.snap

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit bd624a5

Please sign in to comment.