Home

Awesome

hgrep: Human-friendly GREP

CI crate Coverage

hgrep is a grep tool to search files with a given pattern and print the matched code snippets with human-friendly syntax highlighting. This tool brings search results like the code search on GitHub to your local machine. In short, it's something like searching files with ripgrep and showing results with bat.

This is similar to -C option of grep command. hgrep is useful to survey the matches with contexts around them. When some matches are near enough, hgrep prints the lines within one code snippet. Unlike grep -C, hgrep adopts some heuristics around blank lines to determine an efficient number of context lines.

<img src="https://github.com/rhysd/ss/raw/master/hgrep/main.png" alt="screenshot" width="682" />

Example:

# Use built-in subset of ripgrep (optional)
hgrep pattern ./dir

# Read results of grep command via stdin
grep -nH pattern -R ./dir | hgrep
rg -nH pattern ./dir | hgrep

hgrep provides two printers to print match results for your use case. Please see 'bat printer v.s. syntect printer' section for the comparison.

Please see the usage section for more details.

Installation

Binary releases

Visit the releases page and download the zip file for your platform. Unarchive the file and put the executable file in some $PATH directory. Currently, the following targets are supported. If you want a binary for some other platform, feel free to make an issue to request it.

Via Homebrew

By adding hgrep repository as Homebrew tap, hgrep command can be installed and managed via Homebrew. The formula supports x86_64/aarch64 macOS and x86_64 Linux.

brew tap "rhysd/hgrep" "https://github.com/rhysd/hgrep"
brew install hgrep

Note: If you installed Homebrew to a non-default location (e.g. ~/homebrew), you might see some errors. In the case, please try to install hgrep via cargo instead. See #6 for more details.

Via MacPorts

On macOS, you can install hgrep with the following commands through MacPorts:

sudo port selfupdate
sudo port install hgrep

For NetBSD

To install pre-built binaries using the package manager, simply run:

pkgin install hgrep

Or, if you prefer to build from source,

cd /usr/pkgsrc/textproc/hgrep
make install

Via APT package manager on Debian or Ubuntu

Visit the releases page and download .deb package file. It can be installed via dpkg command:

sudo dpkg -i hgrep_v0.3.6-1_amd64.deb

The manual (for man command) and Bash completion is automatically installed. If you're using some other shell, setup the shell completion by yourself. See 'Generate completion scripts' section for more details.

Via cargo package manager

cargo install hgrep

Since cargo builds hgrep command from sources, you can choose your favorite features and drop others by feature flags. For example, if you always use hgrep with reading grep output from stdin and don't use bat printer, only enabling syntect-printer dramatically reduces the number of dependencies, installation time, and binary size.

cargo install hgrep --no-default-features --features syntect-printer

Feature flags

All features are optional and enabled by default. At least bat-printer or syntect-printer needs to be enabled.

FeatureDescription
ripgrepBuilt-in grep implementation built on top of ripgrep as a library. Performance is better than piping rg in some cases.
syntect-printerOur own printer implementation built with syntect library. Performance and output layout are optimized for our use cases.
bat-printerPrinter implementation built on top of bat's pretty printer, which is battle-tested and provides some unique features.

For the differences of bat-printer and syntect-printer, see 'bat printer v.s. syntect printer' section.

Usage

Built-in ripgrep

Optionally hgrep provides built-in grep implementation thanks to ripgrep as a library. It is a subset of rg command.

Built-in ripgrep is a recommended way to use hgrep because it is optimized for the use case.

When a pattern is given, hgrep command search files in given paths with it. When a directory is included in paths, hgrep searches it recursively. When no path is given, hgrep searches the current directory.

hgrep [options...] pattern [paths...]

By default, hgrep shows at least 3 lines and at most 6 lines as context of a match. How many context lines is determined by some heuristics around blank lines for space efficiency. Minimum context lines can be specified by -c and maximum context lines can be specified by -C. If you don't want the heuristics, give the same value to the options like -c 6 -C 6.

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

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.

# 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.

Eating grep -nH output

When no pattern and paths are given in command line arguments, hgrep can take grep results via stdin. Since hgrep expects file paths and line numbers in each line of the output, -nH is necessary at grep command.

grep -nH pattern -R paths... | hgrep [options...]

grep alternative tools like ripgrep, ag, pt, ... are also available because they can output results compatible with grep -nH.

rg -nH pattern paths... | hgrep [options...]

bat printer v.s. syntect printer

hgrep provides two printers to print match results; bat printer and syntect printer. bat printer is a printer implementation built on top of bat's pretty printer. And syntect printer is our own printer implementation built with syntect library. --printer (or -p) flag can specify the printer to print results.

At first, there was bat printer only. And then syntect printer was implemented for better performance and optimized layout.

Pros of each printers

syntect is the default printer.

Themes only available with syntect printer

ayu-darkayu-mirageayu-light
ayu-darkayu-mirageayu-light
CyanidepredawnMaterial
cyanidepredawncyanide

(and more...)

Why performance of syntect printer is better?

Syntax highlighting is very CPU-heavy task. Many regular expression matchings happen at each line. For accurate syntax highlighting, a highlighter needs to parse the syntax from the beginning of file. It means that printing a match at the last line of a file is a much heavier task than printing a match at the first line of the file.

Since syntect printer is designed for calculating syntax highlights per file in parallel, its performance is much better. It's 2x~4x faster than bat printer in some experiments. More match results get better performance.

In contrast, bat is not designed for multi-threads. It's not possible to share bat::PrettyPrinter instance across threads. It means that printing match results including syntax highlighting must be done in a single thread.

syntect printer sequencebat printer sequence

Using pager

When you want a pager to see the output interactively, please pipe the output to external commands like less. $COLUMNS needs to be passed to --term-width option because the terminal width is fixed to 80 characters when stdout is not connected to TTY. If you frequently use a pager, it is a good option to define a wrapper shell function like below:

function hgrep() {
    command hgrep --term-width "$COLUMNS" "$@" | less -R
}

Change color theme and layout

The default color theme is Monokai Extended respecting bat command's default. Other theme can be specified via --theme option. To know names of themes, try --list-themes flag.

hgrep --theme Nord ...

The default layout is 'grid'. To reduce borderlines to use space more efficiently, --no-grid option is available.

hgrep --no-grid ...

When you use bat printer, hgrep respects BAT_THEME and BAT_STYLE environment variable. Theme set to BAT_THEME is used by default. And the grid layout is used when plain or header or numbers is set to BAT_STYLE. syntect printer does not look at these variables. To set default theme, please use a command alias in your shell (See 'Set default command options' for details).

export BAT_THEME=OneHalfDark
export BAT_STYLE=numbers
hgrep -p bat ...

When syntect printer is used, painting background colors is supported with --background flag.

hgrep --background ...

Set default command options

HGREP_DEFAULT_OPTS environment variable is available. Options set to the variable are prepended to the command line arguments when you run hgrep command.

Here are some examples:

# 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...

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

# Use 'bat' printer by default
export HGREP_DEFAULT_OPTS='--printer bat'

The command line arguments in the environment variable are parsed with shlex. Use quotes for including spaces in some command line option arguments. For example:

$Env:HGREP_DEFAULT_OPTS = "--glob '!C:\Program Files'"

Command options

See --help for the full list of available options in your environment.

Text encoding

hgrep supports various encodings thanks to encoding_rs crate.

--encoding (-E) command line option can specify the file encoding explicitly. For example, the following command will assume matched files are encoded with Shift JIS.

hgrep --encoding sjis pattern

In addition, hgrep tries to detect file encodings from BOM. UTF-16LE, UTF-16BE, and UTF-8 can be detected automatically.

When no file encoding is detected from BOM, hgrep assumes files are encoded in UTF-8 as default encoding. If malformed UTF-8 sequences are contained, they are replaced with the replacement character U+FFFD.

<a name="gen-completion-scripts"></a>

Generate completion scripts

Shell completion script for hgrep command is available. --generate-completion-script option generates completion script and prints it to stdout. Bash, Zsh, Fish, PowerShell, Elvish, Nushell are supported. See --help for more details.

Here is an example of setup the completion script on Zsh.

# Let's say we set comps=~/.zsh/site-functions
hgrep --generate-completion-script zsh > ~/.zsh/site-functions/_hgrep
chmod +x ~/.zsh/site-functions/_hgrep

Generate man page

Manual page for hgrep command is available. --generate-man-page flag generates the man page and prints it to stdout.

This is an example to generate man page in /usr/local/share.

hgrep --generate-man-page > /usr/local/share/man/man1/hgrep.1

# See the manual page
man hgrep

Exit status

hgrep command returns the exit status as follows.

StatusDescription
0One or more matches were found
1No match was found
2Some error happened (e.g. IO error)

Terminal color support detection

hgrep automatically detects 24-bit or 256 or 16 colors support of your terminal application by the following logic.

COLORTERM

At first, hgrep checks COLORTERM environment variable. When truecolor or 24bit is set to the variable, 24-bit colors are enabled.

On Linux or macOS or FreeBSD or NetBSD, hgrep tries to detect your terminal's color support from terminfo.

On Windows, hgrep enables 24-bit colors if the Windows version is 10.0.15063 or later becuase OS version 10.0.15063 (Windows 10 1703) started to support 24-bit colors. Otherwise it enables only 16-colors.

TERM

When COLORTERM is not set, hgrep checks TERM environment variable which the terminal name is set.

Fallback

When no color support was detected from COLORTERM nor TERM, htrep eventually falls back to 256 colors, which are most widely supported by popular terminals.

Versioning

At this point the major version is fixed to 0. The minor version is bumped when some breaking changes are added. The patch version is bumped when some new compatible changes are added and/or some bug fixes are added.

Alternatives

Some other alternatives instead of using hgrep.

Small ShellScript to combine ripgrep and bat

ripgrep and bat are well-designed tools so they can be used as building parts of a small script.

rg -nH ... | while IFS= read -r line; do
  # Parse $line and calculate the range of snippet and highlighted lines
  file=...
  lines=...
  range=...

  # Show matched snippet
  bat -H ${lines} -r ${range} ${file}
done

It works fine but hgrep is more optimized for this usage.

Fuzzy finder like fzf with bat preview window

Fuzzy finder like fzf provides a preview window functionality and bat can print the match in the preview window.

grep -nH ... | \
    fzf --preview='bat --pager never --color always -H {2} -r {2}: -p {1}' --delimiter=:

This usage is great when you need the incremental search, but you need to check each preview of matches one by one.

hgrep focuses on surveying all the matches.

Bug reporting

Please create an issue on GitHub. Ensure to describe how to reproduce the bug.

License

hgrep is distributed under the MIT license.