Files
bat-extras/src/batgrep.sh
2020-04-01 12:49:55 -07:00

243 lines
5.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2019 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
# shellcheck disable=SC1090
LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo ".")")/../lib" && pwd)"
BAT="bat"
source "${LIB}/print.sh"
source "${LIB}/pager.sh"
source "${LIB}/opt.sh"
source "${LIB}/opt_hooks.sh"
source "${LIB}/version.sh"
# -----------------------------------------------------------------------------
# Init:
# -----------------------------------------------------------------------------
hook_color
hook_pager
# -----------------------------------------------------------------------------
# Options:
# -----------------------------------------------------------------------------
RG_ARGS=()
BAT_ARGS=()
PATTERN=""
FILES=()
OPT_CASE_SENSITIVITY=''
OPT_CONTEXT_BEFORE=2
OPT_CONTEXT_AFTER=2
OPT_FOLLOW=true
OPT_SNIP=""
OPT_HIGHLIGHT=true
OPT_SEARCH_PATTERN=false
OPT_FIXED_STRINGS=false
BAT_STYLE="header,numbers"
# Set options based on the bat version.
if version_compare "$(bat_version)" -gt "0.12"; then
OPT_SNIP=",snip"
fi
# Parse arguments.
while shiftopt; do
case "$OPT" in
# ripgrep options
-i | --ignore-case) OPT_CASE_SENSITIVITY="--ignore-case" ;;
-s | --case-sensitive) OPT_CASE_SENSITIVITY="--case-sensitive" ;;
-S | --smart-case) OPT_CASE_SENSITIVITY="--smart-case" ;;
-A | --after-context) shiftval; OPT_CONTEXT_AFTER="$OPT_VAL" ;;
-B | --before-context) shiftval; OPT_CONTEXT_BEFORE="$OPT_VAL" ;;
-C | --context)
shiftval
OPT_CONTEXT_BEFORE="$OPT_VAL"
OPT_CONTEXT_AFTER="$OPT_VAL"
;;
-F | --fixed-strings)
OPT_FIXED_STRINGS=true
RG_ARGS+=("$OPT")
;;
-U | --multiline | \
-P | --pcre2 | \
-z | --search-zip | \
-w | --word-regexp | \
--one-file-system | \
--multiline-dotall | \
--ignore | --no-ignore | \
--crlf | --no-crlf | \
--hidden | --no-hidden)
RG_ARGS+=("$OPT")
;;
-E | --encoding | \
-g | --glob | \
-t | --type | \
-T | --type-not | \
-m | --max-count | \
--max-depth | \
--iglob | \
--ignore-file)
shiftval
RG_ARGS+=("$OPT" "$OPT_VAL")
;;
# bat options
# Script options
--no-follow) OPT_FOLLOW=false ;;
--no-snip) OPT_SNIP="" ;;
--no-highlight) OPT_HIGHLIGHT=false ;;
-p | --search-pattern) OPT_SEARCH_PATTERN=true ;;
--no-search-pattern) OPT_SEARCH_PATTERN=false ;;
# Option forwarding
--rg:*) {
if [[ "${OPT:5:1}" = "-" ]]; then
RG_ARGS+=("${OPT:5}")
else
RG_ARGS+=("--${OPT:5}")
fi
if [[ -n "$OPT_VAL" ]]; then
RG_ARGS+=("$OPT_VAL")
fi
} ;;
# ???
-*) {
printc "%{RED}%s: unknown option '%s'%{CLEAR}\n" "$PROGRAM" "$OPT" 1>&2
exit 1
} ;;
# Search
*) {
if [ -z "$PATTERN" ]; then
PATTERN="$OPT"
else
FILES+=("$OPT")
fi
} ;;
esac
done
if [[ -z "$PATTERN" ]]; then
print_error "no pattern provided"
exit 1
fi
# Generate separator.
COLS="$(tput cols)"
SEP="$(printc "%{DIM}%${COLS}s%{CLEAR}" | sed "s/ /─/g")"
# Append ripgrep and bat arguments.
if [[ -n "$OPT_CASE_SENSITIVITY" ]]; then
RG_ARGS+=("$OPT_CASE_SENSITIVITY")
fi
if "$OPT_FOLLOW"; then
RG_ARGS+=("--follow")
fi
if "$OPT_COLOR"; then
BAT_ARGS+=("--color=always")
else
BAT_ARGS+=("--color=never")
fi
if [[ "$OPT_CONTEXT_BEFORE" -eq 0 && "$OPT_CONTEXT_AFTER" -eq 0 ]]; then
OPT_SNIP=""
OPT_HIGHLIGHT=false
fi
# Handle the --search-pattern option.
if "$OPT_SEARCH_PATTERN"; then
if is_pager_less; then
if "$OPT_FIXED_STRINGS"; then
# This strange character is a ^R, or Control-R, character. This instructs
# less to NOT use regular expressions, which is what the -F flag does for
# ripgrep. If we did not use this, then less would match a different pattern
# than ripgrep searched for. See man less(1).
SCRIPT_PAGER_ARGS+=(-p $'\x12'"$PATTERN")
else
SCRIPT_PAGER_ARGS+=(-p "$PATTERN")
fi
elif is_pager_disabled; then
print_error "%s %s %s" \
"The -p/--search-pattern option requires a pager, but" \
"the pager was explicitly disabled by \$BAT_PAGER or the" \
"--paging option."
exit 1
else
print_error "Unsupported pager '%s' for option -p/--search-pattern" \
"$(pager_name)"
exit 1
fi
fi
# -----------------------------------------------------------------------------
# Main:
# -----------------------------------------------------------------------------
main() {
# shellcheck disable=SC2034
FOUND_FILES=()
FOUND=0
FIRST_PRINT=true
LAST_LR=()
LAST_LH=()
LAST_FILE=''
do_print() {
[[ -z "$LAST_FILE" ]] && return 0
# Print the separator.
"$FIRST_PRINT" && echo "$SEP"
FIRST_PRINT=false
# Print the file.
"$BAT" "${BAT_ARGS[@]}" \
"${LAST_LR[@]}" \
"${LAST_LH[@]}" \
--style="${BAT_STYLE}${OPT_SNIP}" \
--paging=never \
--terminal-width="$COLS" \
"$LAST_FILE"
# Print the separator.
echo "$SEP"
}
# shellcheck disable=SC2034
while IFS=':' read -r file line column; do
((FOUND++))
if [[ "$LAST_FILE" != "$file" ]]; then
do_print
LAST_FILE="$file"
LAST_LR=()
LAST_LH=()
fi
# Calculate the context line numbers.
line_start=$((line - OPT_CONTEXT_BEFORE))
line_end=$((line + OPT_CONTEXT_AFTER))
[[ "$line_start" -gt 0 ]] || line_start=''
LAST_LR+=("--line-range=${line_start}:${line_end}")
[[ "$OPT_HIGHLIGHT" = "true" ]] && LAST_LH+=("--highlight-line=${line}")
done < <(rg --with-filename --vimgrep "${RG_ARGS[@]}" --sort path "$PATTERN" "${FILES[@]}")
do_print
# Exit.
if [[ "$FOUND" -eq 0 ]]; then
exit 2
fi
}
pager_exec main
exit $?