Compare commits

..

No commits in common. "master" and "v2020.04.08" have entirely different histories.

80 changed files with 541 additions and 5025 deletions

98
.circleci/config.yml Normal file
View file

@ -0,0 +1,98 @@
version: 2.1
executors:
linux_alpine:
docker:
- image: alpine:latest
environment:
TERM: xterm
commands:
setup:
description: "Set up the environment needed to test and run the scripts."
steps:
- run:
name: "APK: Add repository."
command: |
printf "\n%s\n" "http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
printf "\n%s\n" "http://nl.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories
- run:
name: "APK: Update cache."
command: apk update
- run:
name: "Setup: Install build packages."
command: apk add git bash python2 diffutils ca-certificates curl shfmt
- run:
name: "Setup: Install test packages."
command: apk add util-linux coreutils
- run:
name: "Setup: Install runtime packages."
command: apk add bat ripgrep ncurses
build:
description: "Build the self-contained scripts."
parameters:
minify:
type: string
default: "lib"
steps:
- run:
name: "Build"
command: ./build.sh --minify=<<parameters.minify>> --no-verify
jobs:
build:
executor: linux_alpine
steps:
- setup
- checkout
- build:
minify: "all"
- store_artifacts:
path: bin
test:
executor: linux_alpine
steps:
- setup
- checkout
- build:
minify: "all"
- run:
name: "Test: Unit Tests / Snapshots"
command: ./test.sh --verbose --strict --snapshot:show
test-consistency:
executor: linux_alpine
steps:
- setup
- checkout
- build:
minify: "all"
- run:
name: "Test: Consistency"
command: ./test.sh --compiled --verbose --snapshot:show
test-symlink:
executor: linux_alpine
steps:
- setup
- checkout
- run:
name: "Symlink"
command: ln -s "$PWD/src/batgrep.sh" /tmp/batgrep
- run:
name: "Test: Symlink"
command: /tmp/batgrep 'a' >/dev/null
workflows:
version: 2
default:
jobs:
- build
- test:
requires: [build]
- test-consistency:
requires: [build, test]
- test-symlink:
requires: [build]

View file

@ -1,54 +0,0 @@
name: 'Build'
description: 'Builds bat-extras'
inputs:
manuals:
description: 'Builds manuals'
required: false
default: true
inline:
description: 'Inlines executable names in script'
required: false
default: true
verify:
description: 'Verifies after building'
required: false
default: false
minify:
description: 'Minification mode (none, all, lib)'
required: false
default: 'none'
runs:
using: composite
steps:
- name: Run build script
shell: bash
run: |
args=(
--banner
--minify="${{ inputs.minify }}"
)
if "${{ inputs.manuals }}"; then
args+=(--manuals)
else
args+=(--no-manuals)
fi
if "${{ inputs.verify }}"; then
args+=(--verify)
else
args+=(--no-verify)
fi
if "${{ inputs.inline }}"; then
args+=(--inline)
else
args+=(--no-inline)
fi
# Run the build script.
cd "${{ github.workspace }}"
PATH="${{ runner.temp }}/bin:${PATH}"
bash "${{ github.workspace }}/build.sh" "${args[@]}"

View file

@ -1,31 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2019-2023 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$GITHUB_WORKSPACE"
# -----------------------------------------------------------------------------
# Overrides from release.sh:
# -----------------------------------------------------------------------------
batextras:get_git_workspace() {
printf "%s\n" "${GITHUB_WORKSPACE}"
}
batextras:get_current_commit() {
printf "%s\n" "${GITHUB_SHA:-$(git -C "$GITHUB_WORKSPACE" rev-parse HEAD)}"
}
# -----------------------------------------------------------------------------
# Generate changelog:
# -----------------------------------------------------------------------------
set -euo pipefail
source "${PROJECT_DIR}/release.sh"
batextras:generate_release_notes \
"$(batextras:get_previous_tag_commit)" \
"$(batextras:get_current_commit)"

View file

@ -1,31 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2019-2023 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$GITHUB_WORKSPACE"
# -----------------------------------------------------------------------------
# Overrides from release.sh:
# -----------------------------------------------------------------------------
batextras:get_git_workspace() {
printf "%s\n" "${GITHUB_WORKSPACE}"
}
batextras:get_current_commit() {
printf "%s\n" "${GITHUB_SHA:-$(git -C "$GITHUB_WORKSPACE" rev-parse HEAD)}"
}
# -----------------------------------------------------------------------------
# Generate changelog:
# -----------------------------------------------------------------------------
set -euo pipefail
source "${PROJECT_DIR}/release.sh"
zipball_name="bat-extras-$(batextras:get_version | sed 's/\.//')"
zipball="${PROJECT_DIR}/${zipball_name}.zip"
batextras:create_package "$zipball"

View file

@ -1,92 +0,0 @@
name: 'Install dependencies'
description: 'Installs all the dependencies needed.'
inputs:
build:
description: 'Install build dependencies.'
required: false
default: true
test:
description: 'Install test dependencies.'
required: true
default: false
version_bat:
description: 'The version of bat to install.'
required: false
default: "latest"
version_ripgrep:
description: 'The version of ripgrep to install.'
required: false
default: "latest"
runs:
using: "composite"
steps:
- name: Create directories
shell: bash
run: |
test -d "${RUNNER_TEMP}/bin" || mkdir -p "${RUNNER_TEMP}/bin"
test -d "${RUNNER_TEMP}/dl" || mkdir -p "${RUNNER_TEMP}/dl"
- name: Install shfmt (build dependency)
shell: bash
if: ${{ env.ACT || inputs.build == 'true' }}
run: |
curl \
--silent \
--location \
--output "${RUNNER_TEMP}/bin/shfmt" \
"https://github.com/patrickvane/shfmt/releases/download/master/shfmt_linux_amd64"
chmod +x "${RUNNER_TEMP}/bin/shfmt"
- name: Download bat (test dependency)
uses: dsaltares/fetch-gh-release-asset@master
if: ${{ env.ACT || inputs.test == 'true' }}
with:
file: "bat-v[0-9\\.]+-x86_64-unknown-linux-gnu.tar.gz"
repo: "sharkdp/bat"
# version: "${{ inputs.version_bat || "latest" }}"
regex: true
target: ".dl/"
- name: Install bat
shell: bash
if: ${{ env.ACT || inputs.test == 'true' }}
run: |
tar -xf \
"${{ github.workspace }}/.dl"/bat-*.tar.* \
-C "${{ runner.temp }}/dl/"
find "${{ runner.temp }}/dl" \
-type f -iname "bat" \
-exec mv {} "${RUNNER_TEMP}/bin/" \;
- name: Download ripgrep (test dependency)
uses: dsaltares/fetch-gh-release-asset@master
if: ${{ env.ACT || inputs.test == 'true' }}
with:
file: "ripgrep_[0-9\\.-]+_amd64.deb"
repo: "BurntSushi/ripgrep"
# version: "${{ inputs.version_ripgrep || "latest" }}"
regex: true
target: ".dl/"
- name: Install ripgrep
shell: bash
if: ${{ env.ACT || inputs.test == 'true' }}
run: |
dpkg-deb -x \
"${{ github.workspace }}/.dl"/ripgrep_*.deb \
"${{ runner.temp }}/dl/"
find "${{ runner.temp }}/dl" \
-type f -path "*/bin/rg" \
-exec mv {} "${{ runner.temp }}/bin/" \;
- name: Set executable permissions
shell: bash
run: |
chmod -R +x "${{ runner.temp }}/bin"

View file

@ -1,33 +0,0 @@
name: 'Test'
description: 'Tests bat-extras'
inputs:
strict:
description: 'Tests should be run under strict mode'
required: false
default: false
compiled:
description: 'Test scripts that have been built'
required: false
default: false
runs:
using: composite
steps:
- name: Run tests
shell: bash
run: |
args=()
if "${{ inputs.compiled }}"; then
args+=(--compiled)
fi
if "${{ inputs.strict }}"; then
args+=(--strict)
fi
cd "${{ github.workspace }}"
PATH="${{ runner.temp }}/bin:${PATH}"
bash "${{ github.workspace }}/test.sh" "${args[@]}" \
--verbose --snapshot:show

View file

@ -1,45 +0,0 @@
name: Release
on:
push:
tags: ['*']
jobs:
"Release":
runs-on: ubuntu-latest
steps:
- name: Check out sources
uses: actions/checkout@v3
- name: Install build dependencies
uses: ./.github/actions/install-dependencies
with:
build: true
test: true
- name: Build
uses: ./.github/actions/build
with:
minify: lib
manuals: true
verify: true
inline: false
- name: Create zipball
run: bash "${{ github.workspace }}/.github/actions/build/create-zipball.sh"
- name: Generate changelog
id: changelog
run: |
output_file="release-notes.md"
output_title="Release: $(date '+%Y-%m-%d')"
bash "${{ github.workspace }}/.github/actions/build/create-release-notes.sh" | tee "${output_file}"
echo "file=${output_file}" >> "$GITHUB_OUTPUT"
echo "title=${output_title}" >> "$GITHUB_OUTPUT"
- name: Create release
if: ${{ !env.ACT }}
uses: ncipollo/release-action@v1
with:
artifacts: "bat-extras-*.zip"
bodyFile: "${{steps.changelog.outputs.file}}"
name: "${{steps.changelog.outputs.title}}"

View file

@ -1,96 +0,0 @@
name: Test
on:
push: {}
pull_request:
types: [opened, synchronize]
jobs:
"Build":
runs-on: ubuntu-latest
steps:
- name: Check out sources
uses: actions/checkout@v3
- name: Install build dependencies
uses: ./.github/actions/install-dependencies
with:
build: true
test: false
- name: Build artifacts
uses: ./.github/actions/build
with:
minify: lib
manuals: true
verify: false
inline: false
- name: Upload artifacts
uses: actions/upload-artifact@v3
if: ${{ !env.ACT && !failure() }}
with:
path: |
${{ github.workspace }}/bin/
${{ github.workspace }}/doc/
"Test":
runs-on: ubuntu-latest
steps:
- name: Check out sources
uses: actions/checkout@v3
- name: Install test dependencies
uses: ./.github/actions/install-dependencies
with:
build: false
test: true
- name: Test scripts
uses: ./.github/actions/test
"Test_Consistency":
runs-on: ubuntu-latest
needs: "Test"
steps:
- name: Check out sources
uses: actions/checkout@v3
- name: Install dependencies
uses: ./.github/actions/install-dependencies
with:
build: true
test: true
- name: Build scripts
uses: ./.github/actions/build
with:
minify: lib
manuals: false
verify: false
inline: false
- name: Test built scripts
uses: ./.github/actions/test
with:
compiled: true
"Test_Symlinks":
runs-on: ubuntu-latest
needs: "Test"
env:
BAT_PAGER: 'cat'
steps:
- name: Check out sources
uses: actions/checkout@v3
- name: Install dependencies
uses: ./.github/actions/install-dependencies
with:
build: false
test: true
- name: Prepare symlinks
run: |
chmod +x "${{ github.workspace }}/src/batgrep.sh"
ln -s "${{ github.workspace }}/src/batgrep.sh" "${{ runner.temp }}/absolute-batgrep"
(cd "${{ github.workspace }}" && ln -s "src/batgrep.sh" relative-batgrep)
- name: Test absolute symlink
run: |
PATH="${{ runner.temp }}/bin:${PATH}"
"${{ runner.temp }}/absolute-batgrep" 'a' <<< 'abc'
- name: Test relative symlink
run: |
PATH="${{ runner.temp }}/bin:${PATH}"
"${{ github.workspace }}/relative-batgrep" 'a' <<< 'abc'

9
.gitignore vendored
View file

@ -4,14 +4,9 @@
# Developer
.idea
.vscode
*.iml
# Project
/.circleci/.config.yml
/bin
/man
# Side-Effects
/test/data/.config/fish
/test/data/.local/share/fish
.download
bin

@ -1 +1 @@
Subproject commit 6b97e0a531f77d2e1f10f48ebb68d4033d69e04d
Subproject commit f66c4e03b4577317aec6a739c42890334aefe6b9

View file

@ -1,18 +0,0 @@
# Contributing
Are you interested in contributing to `bat-extras`? That's great!
All we ask is that you keep a few things in mind:
## DOs:
- Use Bashisms whenever possible (unless it would make the code unclear). These are scripts written specifically for the `bash` shell, and they should take advantage of built-in functionality instead of spawning external processes.
- Use `awk` or `sed` when it would be faster or less verbose than only using `bash` builtins.
## DON'Ts:
- Include crude or offensive language inside issues or scripts.
- Use GNU/BSD-only features in external programs like `sed` or `awk`.
- Use `bash` features that require a Bash version newer than 3.2.
- Use `head` in a pipe. See [Issue #19](https://github.com/eth-p/bat-extras/issues/19) for more details.
- Use any external program that isn't likely to come installed by default (e.g. avoid `perl` and `python`).
- Automatically reformat scripts unless it's a pull request specifically intended for reformatting. The formatting style that we use differs from what `shfmt` and other shell script formatters will emit.

View file

@ -18,12 +18,6 @@ Read system manual pages (`man`) using `bat` as the manual page formatter.
### [`batpipe`](doc/batpipe.md)
A `less` (and soon `bat`) preprocessor for viewing more types of files in the terminal.
### [`batwatch`](doc/batwatch.md)
Watch for changes in one or more files, and print them with `bat`.
@ -31,12 +25,6 @@ Watch for changes in one or more files, and print them with `bat`.
### [`batdiff`](doc/batdiff.md)
Diff a file against the current git index, or display the diff between two files.
<u>Requirements:</u> `bat`, `delta` (optional)
### [`prettybat`](doc/prettybat.md)
Pretty-print source code and highlight it with `bat`.
@ -46,67 +34,16 @@ Pretty-print source code and highlight it with `bat`.
## Installation via Package Manager
### Homebrew
All of the `bat-extras` scripts can be installed with `brew install bat-extras`.
If you would prefer to only install the specific scripts you need, you can use the `eth-p/software` tap to install individual scripts: `brew install eth-p/software/bat-extras-[SCRIPT]`
### MacPorts
The `bat-extras` scripts can also be installed via [MacPorts](https://www.macports.org) on macOS:
```bash
sudo port install bat-extras
```
Port info [here](https://ports.macports.org/port/bat-extras/).
### Pacman
`bat-extras` is [officially available](https://archlinux.org/packages/extra/any/bat-extras/) on the Arch extra repository!
If you have the extra repository enabled, you can install `bat-extras` by running:
```bash
pacman -S bat-extras
```
### Gentoo
`bat-extras` is available on **Gentoo's Guru Overlay** as `sys-apps/bat-extras`.
To install, first make sure you've added the [Gentoo Guru Overlay](https://wiki.gentoo.org/wiki/Project:GURU) to your local repositories, then emerge accordingly...
```bash
emerge sys-apps/bat-extras
```
### Fedora (Unofficial)
`bat-extras` is available in an unofficial Fedora Copr
[repository](https://copr.fedorainfracloud.org/coprs/awood/bat-extras/).
**Note**: this package does not contain `prettybat` since `prettier` is not yet
packaged for Fedora.
Install the Copr plugin, enable the repository, and then install the package
by running:
```bash
dnf install dnf-plugins-core
dnf copr enable awood/bat-extras
dnf install bat-extras
```
**Homebrew:** `brew install eth-p/software/bat-extras`
&nbsp;
## Installation
[![Test](https://github.com/eth-p/bat-extras/actions/workflows/test.yaml/badge.svg)](https://github.com/eth-p/bat-extras/actions/workflows/test.yaml)
## Installation (![CircleCI](https://circleci.com/gh/eth-p/bat-extras.svg?style=svg))
The scripts in this repository are designed to run as-is, provided that they aren't moved around.
This means that you're free to just symlink `src/[script].sh` to your local bin folder.
If you would rather have *faster*, self-contained scripts that you can place and run anywhere, you can use the `build.sh` script to create (and optionally install) them.
If you would rather have self-contained scripts that you can place and run anywhere, you can use the `build.sh` script to create (and optionally install) them.
&nbsp;
@ -143,27 +80,13 @@ If you only want to install a single script, you can run the build process and c
**Manuals:**
The build script will automatically generate a `man` page for each of the markdown documentation files.
This is a beta feature that uses a non-compliant Markdown "parser" written in Bash, and there is no guarantee towards the quality of the generated manual pages. If you do not want to generate manual files, you can provide the `--no-manuals` option to disable manual file generation.
**Alternate Executable:**
Depending on the distribution, bat may have been renamed to avoid package conflicts.
If you wish to use these scripts on a distribution where this is the case, there is an `--alternate-executable=NAME` option which will build the scripts to use an alternate executable name.
You may also specify alternate executables for `ripgrep`, `delta`, `fzf`, or `git` with `--alternate-executable:PROGRAM NAME` where `PROGRAM` is one the aforementioned programs. Note that doing so may cause verification to fail.
**Verification:**
The build script will attempt to verify the correctness of the "bin" scripts by comparing their output with their source counterparts. It is recommended to let it do this, but you can disable verification with the `--no-verify` option.
&nbsp;
## Contributing
If you would like to contribute to `bat-extras`, please feel free to [open an issue on GitHub](https://github.com/eth-p/bat-extras/issues), or make a pull request. If you do the latter, please keep our [contributing guidelines](./CONTRIBUTING.md) in mind.

434
build.sh
View file

@ -1,122 +1,19 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2019-2023 eth-p | MIT License
# 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
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Build-as-a-Library Functions:
# -----------------------------------------------------------------------------
# Redefines a function to print a constant string whenever called.
# This is used for lazy-loading of some getter functions.
#
# Arguments:
# 1 -- The function name.
# 2 -- The constant string to print.
#
# Output:
# The constant string.
batextras:lazy_done() {
eval "$(printf "%s() { printf \"%%s\n\" %q; }" "$1" "$2")"
"$1"
}
# Checks to see if a function is defined.
# Arguments:
# 1 -- The function name.
# If prefixed with "::", it will use "batextras:" as a namespace.
batextras:is_function_defined() {
local name="$1"
if [[ "${name:0:2}" = "::" ]]; then name="batextras:${name:2}"; fi
[[ "$(type -t "$name" || echo 'undefined')" = "function" ]]
return $?
}
# Prints the path to the project directory.
if ! batextras:is_function_defined ::get_project_directory; then
batextras:get_project_directory() {
batextras:lazy_done "${FUNCNAME[0]}" \
"$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
}
fi
# Prints the path to the project source directory.
if ! batextras:is_function_defined ::get_source_directory; then
batextras:get_source_directory() {
batextras:lazy_done "${FUNCNAME[0]}" \
"$(batextras:get_project_directory)/src"
}
fi
# Prints the path to the project output directory for executables.
if ! batextras:is_function_defined ::get_output_bin_directory; then
batextras:get_output_bin_directory() {
batextras:lazy_done "${FUNCNAME[0]}" \
"$(batextras:get_project_directory)/bin"
}
fi
# Prints the path to the project output directory for manuals.
if ! batextras:is_function_defined ::get_output_man_directory; then
batextras:get_output_man_directory() {
batextras:lazy_done "${FUNCNAME[0]}" \
"$(batextras:get_project_directory)/man"
}
fi
# Prints the path to the project directory for documentation.
if ! batextras:is_function_defined ::get_docs_directory; then
batextras:get_docs_directory() {
batextras:lazy_done "${FUNCNAME[0]}" \
"$(batextras:get_project_directory)/doc"
}
fi
# Prints the declared version (in version.txt).
if ! batextras:is_function_defined ::get_version; then
batextras:get_version() {
batextras:lazy_done "${FUNCNAME[0]}" \
"$(cat "$(batextras:get_project_directory)/version.txt")"
}
fi
# Prints the paths for all source scripts in this project.
#
# Output:
# One line for each script with the full path to the script.
if ! batextras:is_function_defined ::get_source_paths; then
batextras:get_source_paths() {
for file in "$(batextras:get_source_directory)"/*.sh; do
printf "%s\n" "$file"
file_bin="$(basename -- "$file" ".sh")"
done
}
fi
# -----------------------------------------------------------------------------
# Main:
# Only run everything past this point if the script is not sourced.
# -----------------------------------------------------------------------------
(return 0 2>/dev/null) && return 0
HERE="$(batextras:get_project_directory)"
SRC="$(batextras:get_source_directory)"
BIN="$(batextras:get_output_bin_directory)"
MAN="$(batextras:get_output_man_directory)"
MAN_SRC="$(batextras:get_docs_directory)"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BIN="$HERE/bin"
SRC="$HERE/src"
LIB="$HERE/lib"
source "${LIB}/print.sh"
source "${LIB}/opt.sh"
source "${LIB}/constants.sh"
source "${HERE}/mdroff.sh"
# -----------------------------------------------------------------------------
set -eo pipefail
exec 3>&1
# Runs the next build step.
#
@ -137,101 +34,32 @@ next() {
# Prints a build step message.
smsg() {
case "$2" in
"SKIP") printc_msg " %{YELLOW} %{DIM}%s [skipped]%{CLEAR}\n" "$1" ;;
*) printc_msg " %{YELLOW} %s...%{CLEAR}\n" "$1" ;;
"SKIP") printc " %{YELLOW} %{DIM}%s [skipped]%{CLEAR}\n" "$1" 1>&2 ;;
*) printc " %{YELLOW} %s...%{CLEAR}\n" "$1" 1>&2 ;;
esac
}
# Prints a message to STDOUT (via FD 3).
# Works the same as printc.
printc_msg() {
printc "$@" 1>&3
}
# Prints a message to STDERR.
# Works the same as printc.
printc_err() {
printc "$@" 1>&2
}
# Escapes a sed pattern.
# Arguments:
# 1 -- The pattern.
#
# Output:
# The escaped string.
sed_escape() {
sed 's/\([][\\\/\^\$\*\.\-]\)/\\\1/g' <<< "$1"
}
# Checks if the output scripts will be minified.
# Arguments:
# 1 "all" -- All scripts will be minified.
# "any" -- Any scripts will be minified.
# "lib" -- Library scripts will be minified.
# "unsafe" -- Unsafe minifications will be applied.
will_minify() {
case "$1" in
all)
[[ "$OPT_MINIFY" =~ ^all($|\+.*) ]]
return $? ;;
unsafe)
[[ "$OPT_MINIFY" =~ ^.*+unsafe(\+.*)*$ ]]
return $? ;;
lib)
[[ "$OPT_MINIFY" =~ ^lib($|\+.*) ]]
return $? ;;
any|"")
[[ "$OPT_MINIFY" != "none" ]]
return $? ;;
none)
! will_minify any
return $? ;;
esac
return 1
}
# Generates the banner for the output files.
#
# Output:
# The contents of banner.txt
generate_banner() {
local step="$1"
if ! "$OPT_BANNER"; then
return 0
fi
# Don't run it unless the comments are removed or hidden.
if ! { will_minify all || "$OPT_COMPRESS"; }; then
return 0
fi
# Only run it in the compression step if both minifying and compressing.
if will_minify all && "$OPT_COMPRESS" && [[ "$step" != "step_compress" ]]; then
return 0
fi
# Write the banner.
bat "${HERE}/banner.txt"
}
# Build step: read
# Reads the file from its source.
#
# Arguments:
# 1 -- The source file.
# 2 -- A description of what is being read.
#
# Output:
# The file contents.
step_read() {
local what=""
if [[ -n "${2:-}" ]]; then
what=" $2"
fi
cat "$1"
smsg "Reading${what}"
smsg "Reading"
}
# Build step: preprocess
@ -268,14 +96,13 @@ step_preprocess() {
# Output:
# The minified file contents.
step_minify() {
if ! will_minify all; then
if [[ "$OPT_MINIFY" =~ ^all($|+.*) ]]; then
cat
smsg "Minifying" "SKIP"
return 0
fi
printf "#!/usr/bin/env bash\n"
generate_banner step_minify
pp_minify | pp_minify_unsafe
smsg "Minifying"
}
@ -298,7 +125,6 @@ step_compress() {
local wrapper
wrapper="$({
printf '#!/usr/bin/env bash\n'
generate_banner step_compress
printf "(exec -a \"\$0\" bash -c 'eval \"\$(cat <&3)\"' \"\$0\" \"\$@\" 3< <(dd bs=1 if=\"\$0\" skip=::: 2>/dev/null | gunzip)); exit \$?;\n"
})"
@ -324,7 +150,7 @@ step_write() {
smsg "Building"
}
# Build step: write_install
# Build step: write
# Optionally writes the output script to a file.
#
# Arguments:
@ -335,6 +161,7 @@ step_write() {
#
# Output:
# The file contents.
step_write_install() {
if [[ "$OPT_INSTALL" != true ]]; then
cat
@ -347,50 +174,8 @@ step_write_install() {
smsg "Installing"
}
# Build step: manpage_install
# Optionally writes the manpage to a gzipped file.
#
# Arguments:
# 1 -- The file to write to.
#
# Input:
# The file contents.
#
# Output:
# The file contents.
step_manpage_install() {
if [[ "$OPT_INSTALL" != true ]]; then
cat
smsg "Installing manual" "SKIP"
return 0
fi
gzip | tee "$1"
smsg "Installing manual"
}
# Build step: manpage_generate
# Generates a manpage document from a markdown document.
#
# Input:
# The markdown doc contents.
#
# Output:
# The roff manpage contents.
step_manpage_generate() {
if [[ "$OPT_MANUALS" != true ]]; then
cat
smsg "Generating manual" "SKIP"
return 0
fi
(mdroff)
smsg "Generating manual"
}
# -----------------------------------------------------------------------------
# Preprocessor:
# -----------------------------------------------------------------------------
# Preprocessor.
# Consolidates all scripts into a single file.
# This follows all `source "${LIB}/..."` files and embeds them into the script.
@ -423,10 +208,10 @@ pp_consolidate__do() {
# Embed the script.
echo "${indent}# --- BEGIN LIBRARY FILE: ${BASH_REMATCH[1]} ---"
{
if will_minify lib; then
if [[ "$OPT_MINIFY" = "lib" ]]; then
pp_strip_comments | pp_minify | pp_minify_unsafe
else
pp_strip_copyright | pp_strip_separators
cat
fi
} < <(pp_consolidate__do "$((depth + 1))" < "$script") | sed "s/^/${indent}/"
echo "${indent}# --- END LIBRARY FILE ---"
@ -439,13 +224,13 @@ pp_consolidate__do() {
}
# Inlines constants:
# EXECUTABLE_*
# EXECUTABLE_BAT
# PROGRAM_*
pp_inline_constants() {
local constants=("PROGRAM")
local constants=("EXECUTABLE_BAT" "PROGRAM")
# Determine the PROGRAM_ constants.
local nf_constants="$( ( set -o posix ; set) | grep '^\(PROGRAM_\|EXECUTABLE_\)' | cut -d'=' -f1)"
local nf_constants="$( ( set -o posix ; set) | grep '^PROGRAM_' | cut -d'=' -f1)"
local line
while read -r line; do
constants+=("$line")
@ -465,26 +250,15 @@ pp_inline_constants() {
sed "${constants_pattern}"
}
# Strips comments from a Bash source file.
pp_strip_comments() {
sed '/^[[:space:]]*#.*$/d'
}
# Strips copyright comments from the start of a Bash source file.
pp_strip_copyright() {
awk '/^#/ {if(!p){ next }} { p=1; print $0 }'
}
# Strips separator comments from the start of a Bash source file.
pp_strip_separators() {
awk '/^#\s*-{5,}/ { next; } {print $0}'
}
# Minify a Bash source file.
# https://github.com/mvdan/sh
pp_minify() {
if will_minify none; then
if [[ "$OPT_MINIFY" = "none" ]]; then
cat
return
fi
@ -497,7 +271,7 @@ pp_minify() {
# Right now, this doesn't do anything.
# This should be applied after shfmt minification.
pp_minify_unsafe() {
if ! will_minify unsafe; then
if ! [[ "$OPT_MINIFY" =~ ^.*+unsafe(+.*)*$ ]]; then
cat
return 0
fi
@ -506,161 +280,76 @@ pp_minify_unsafe() {
}
# -----------------------------------------------------------------------------
# Options:
# -----------------------------------------------------------------------------
# Options.
OPT_INSTALL=false
OPT_COMPRESS=false
OPT_VERIFY=true
OPT_BANNER=true
OPT_MANUALS=true
OPT_INLINE=true
OPT_MINIFY="lib"
OPT_PREFIX="/usr/local"
EXECUTABLE_BAT="$(basename -- "$EXECUTABLE_BAT")"
ALT_EXECS=()
BUILD_FILTER=()
OPT_BAT="bat"
DOCS_URL="https://github.com/eth-p/bat-extras/blob/master/doc"
DOCS_MAINTAINER="eth-p <eth-p@hidden.email>"
# -----------------------------------------------------------------------------
# Use a different default prefix when running on Termux.
if [[ "$(uname -o)" = "Android" ]] && [[ -n "${TERMUX_VERSION:-}" ]]; then
OPT_PREFIX="/data/data/com.termux/files/usr/"
else
OPT_PREFIX="/usr/local"
fi
# -----------------------------------------------------------------------------
# Parse arguments.
while shiftopt; do
# shellcheck disable=SC2034
case "$OPT" in
--install) OPT_INSTALL=true ;;
--compress) OPT_COMPRESS=true ;;
--manuals) OPT_MANUALS="${OPT_VAL:-true}" ;;
--no-manuals) OPT_MANUALS=false ;;
--verify) OPT_VERIFY=true ;;
--no-verify) OPT_VERIFY=false ;;
--banner) OPT_BANNER=true ;;
--no-banner) OPT_BANNER=false ;;
--inline) OPT_INLINE=true ;;
--no-inline) OPT_INLINE=false ;;
--prefix) shiftval; OPT_PREFIX="$OPT_VAL" ;;
--alternate-executable) shiftval; ALT_EXECS+=("bat"); EXECUTABLE_BAT="$OPT_VAL" ;;
--alternate-executable:bat) shiftval; ALT_EXECS+=("bat"); EXECUTABLE_BAT="$OPT_VAL" ;;
--alternate-executable:ripgrep) shiftval; ALT_EXECS+=("ripgrep"); EXECUTABLE_RIPGREP="$OPT_VAL" ;;
--alternate-executable:delta) shiftval; ALT_EXECS+=("delta"); EXECUTABLE_DELTA="$OPT_VAL" ;;
--alternate-executable:fzf) shiftval; ALT_EXECS+=("fzf"); EXECUTABLE_FZF="$OPT_VAL" ;;
--alternate-executable:git) shiftval; ALT_EXECS+=("git"); EXECUTABLE_GIT="$OPT_VAL" ;;
--alternate-executable) shiftval; OPT_BAT="$OPT_VAL" ;;
--minify) shiftval; OPT_MINIFY="$OPT_VAL" ;;
# Print scripts.
--show:source-paths) get_source_paths; exit 0 ;;
# Unknown options.
*)
if ! [[ -f "${SRC}/${OPT}.sh" ]]; then
printc_err "%{RED}%s: unknown option '%s'%{CLEAR}" "$PROGRAM" "$OPT"
exit 1
fi
BUILD_FILTER+=("$OPT")
printc "%{RED}%s: unknown option '%s'%{CLEAR}" "$PROGRAM" "$OPT"
exit 1
;;
esac
done
if [[ "${#ALT_EXECS[@]}" -gt 0 ]]; then
printc_msg "%{YELLOW}Building executable scripts with alternate executables for:%{CLEAR}\n"
printc_msg "%{YELLOW} - %{CLEAR}%s\n" "${ALT_EXECS[@]}"
printc_msg "\n"
if ! command -v "$EXECUTABLE_BAT" &>/dev/null; then
printc_err "%{YELLOW}WARNING: Bash cannot execute bat's executable file.\n"
printc_err "%{YELLOW} The finished scripts may not run properly.%{CLEAR}\n"
if [[ "$OPT_BAT" != "bat" ]]; then
printc "%{YELLOW}Building executable scripts with an alternate bat executable at %{CLEAR}%s%{YELLOW}.%{CLEAR}\n" "$OPT_BAT" 1>&2
if ! command -v "$OPT_BAT"; then
printc "%{YELLOW}WARNING: Bash cannot execute the specified file.\n" 1>&2
printc "%{YELLOW} The finished scripts may not run properly.%{CLEAR}\n" 1>&2
fi
# shellcheck disable=SC2034
printc_msg "\n"
EXECUTABLE_BAT="$OPT_BAT"
printc "\n" 1>&2
fi
if [[ "$OPT_INSTALL" = true ]]; then
printc_msg "%{YELLOW}Installing to %{MAGENTA}%s%{YELLOW}.%{CLEAR}\n" "$OPT_PREFIX"
printc "%{YELLOW}Installing to %{MAGENTA}%s%{YELLOW}.%{CLEAR}\n" "$OPT_PREFIX" 1>&2
else
printc_msg "%{YELLOW}This will not install the script.%{CLEAR}\n"
printc_msg "%{YELLOW}Use %{BLUE}--install%{YELLOW} for a global install.%{CLEAR}\n\n"
fi
if [[ "$OPT_INLINE" = false ]]; then
# Prevent full executable paths from being inlined.
while read -r exec; do
declare "$exec=$(basename "${!exec}")"
done < <(set | grep '^EXECUTABLE' | cut -d'=' -f1)
printc "%{YELLOW}This will not install the script.%{CLEAR}\n" 1>&2
printc "%{YELLOW}Use %{BLUE}--install%{YELLOW} for a global install.%{CLEAR}\n\n" 1>&2
fi
# -----------------------------------------------------------------------------
# Check for resources.
if ! will_minify none && ! command -v shfmt &>/dev/null; then
printc_err "%{RED}Warning: cannot find shfmt. Unable to minify scripts.%{CLEAR}\n"
[[ -d "$BIN" ]] || mkdir "$BIN"
if [[ "$OPT_MINIFY" != "none" ]] && ! command -v shfmt &>/dev/null; then
printc "%{RED}Warning: cannot find shfmt. Unable to minify scripts.%{CLEAR}\n"
OPT_MINIFY=none
fi
# -----------------------------------------------------------------------------
# Check target directories exist.
[[ -d "$BIN" ]] || mkdir -p "$BIN"
if "$OPT_INSTALL"; then
[[ -d "${OPT_PREFIX}/bin" ]] || mkdir -p "${OPT_PREFIX}/bin"
[[ "$OPT_MANUALS" = "true" && ! -d "${OPT_PREFIX}/share/man/man1" ]] \
&& mkdir -p "${OPT_PREFIX}/share/man/man1"
fi
if [[ "$OPT_MANUALS" = "true" ]]; then
[[ -d "$MAN" ]] || mkdir -p "$MAN"
fi
# -----------------------------------------------------------------------------
# Find files.
SOURCES=()
printc_msg "%{YELLOW}Preparing scripts...%{CLEAR}\n"
while read -r file; do
file_bin="$(basename -- "$file" ".sh")"
buildable=false
if ! "$buildable" && [[ "${#BUILD_FILTER[@]}" -eq 0 ]]; then
buildable=true
elif ! "$buildable"; then
for buildable_file in "${BUILD_FILTER[@]}"; do
if [[ "$buildable_file" = "$file_bin" ]]; then
buildable=true
break
fi
done
fi
# If that one is allowed to build, add it to the sources list.
if "$buildable"; then
SOURCES+=("$file")
else
printc_msg " %{YELLOW}Skipping %{MAGENTA}%s%{CLEAR}\n" "$(basename "$file_bin")"
fi
done < <(batextras:get_source_paths)
if [[ "${#BUILD_FILTER[@]}" -gt 0 ]]; then
printf "\n"
fi
printc "%{YELLOW}Preparing scripts...%{CLEAR}\n" 1>&2
for file in "$SRC"/*.sh; do
SOURCES+=("$file")
done
# -----------------------------------------------------------------------------
# Build files.
printc_msg "%{YELLOW}Building scripts...%{CLEAR}\n"
printc "%{YELLOW}Building scripts...%{CLEAR}\n" 1>&2
file_i=0
file_n="${#SOURCES[@]}"
for file in "${SOURCES[@]}"; do
@ -670,7 +359,7 @@ for file in "${SOURCES[@]}"; do
PROGRAM="$filename"
PROGRAM_VERSION="$(<"${HERE}/version.txt")"
printc_msg " %{YELLOW}[%s/%s] %{MAGENTA}%s%{CLEAR}\n" "$file_i" "$file_n" "$file"
printc " %{YELLOW}[%s/%s] %{MAGENTA}%s%{CLEAR}\n" "$file_i" "$file_n" "$file" 1>&2
step_read "$file" |
next step_preprocess |
next step_minify |
@ -678,59 +367,46 @@ for file in "${SOURCES[@]}"; do
next step_write "${BIN}/${filename}" |
next step_write_install "${OPT_PREFIX}/bin/${filename}" |
cat >/dev/null
# Build manuals.
if [[ -f "${HERE}/doc/${filename}.md" && "$OPT_MANUALS" = "true" ]]; then
step_read "${HERE}/doc/${filename}.md" "manual" |
next step_manpage_generate |
next step_write "${MAN}/${filename}.1" |
next step_manpage_install "${OPT_PREFIX}/share/man/man1/${filename}.1.gz" |
cat >/dev/null
fi
done
# -----------------------------------------------------------------------------
# Verify files by running the tests.
if "$OPT_VERIFY"; then
printc_msg "\n%{YELLOW}Verifying scripts...%{CLEAR}\n"
printc "\n%{YELLOW}Verifying scripts...%{CLEAR}\n" 1>&2
# Run the tests.
FAIL=0
SKIP=0
while read -r action data1 data2 splat; do
[[ "$action" == "result" ]] || continue
printf "\x1B[G\x1B[K%s" "$data1" 1>&2
case "$data2" in
fail)
printc_err "\x1B[G\x1B[K%s failed.\n" "$data1"
printf " failed.\n" 1>&2
((FAIL++)) || true
;;
skip)
printc_msg "\x1B[G\x1B[K%s skipped.\n" "$data1"
((SKIP++)) || true
;;
*)
printc_msg "\x1B[G\x1B[K%s" "$data1"
;;
esac
done < <("${HERE}/test.sh" --compiled --porcelain --jobs=8)
done < <("${HERE}/test.sh" --compiled --porcelain)
# Print the overall result.
printc_msg "\x1B[G\x1B[K"
printf "\x1B[G\x1B[K%s" 1>&2
if [[ "$FAIL" -ne 0 ]]; then
printc_err "%{RED}%s\n" "One or more tests failed."
printc_msg "\x1B[A\x1B[G\x1B[K%{RED}%s\n" "One or more tests failed."
printc_err "%{RED}%s%{CLEAR}\n" "Run './test.sh --failed' for more detailed information."
printc "%{RED}One or more tests failed.\n" 1>&2
printc "Run ./test.sh for more detailed information.%{CLEAR}\n" 1>&2
exit 1
fi
if [[ "$SKIP" -gt 0 ]]; then
printc_err "%{CYAN}One or more tests were skipped.\n"
printc_err "Run ./test.sh for more detailed information.%{CLEAR}\n"
printc "%{CYAN}One or more tests were skipped.\n" 1>&2
printc "Run ./test.sh for more detailed information.%{CLEAR}\n" 1>&2
fi
printc_msg "%{YELLOW}Verified successfully.%{CLEAR}\n"
printc "%{YELLOW}Verified successfully.%{CLEAR}\n" 1>&2
fi

View file

@ -1,59 +0,0 @@
# bat-extras: batdiff
Diff a file against the current git index, or display the diff between two files.
This script supports using [delta](https://github.com/dandavison/delta) as an alternative highlighter for diffs.
## Usage
batdiff [OPTIONS] FILE
batdiff [OPTIONS] FILE OTHER_FILE
batdiff --staged
## Environment
| Variable | Description |
| ------------------------ | ------------------------------------------------ |
| `BATDIFF_USE_DELTA=true` | If `delta` is installed, use `delta` by default. |
## Options
| Short | Long | Description |
| ----- | ----------------------------- | ------------------------------------------------------------ |
| `-C` | `--context=[LINES]` | The number of lines to show before and after the differing lines. |
| | `--delta` | Display diffs using `delta`. |
| | `--color` | Force color output. |
| | `--no-color` | Force disable color output. |
| | `--paging=["never"/"always"]` | Enable/disable paging. |
| | `--pager=[PAGER]` | Specify the pager to use. |
| | `--terminal-width=[COLS]` | Generate output for the specified terminal width. |
| | `--staged` | Show staged changes. |
## Installation
This script is a part of the `bat-extras` suite of scripts. You can find install instructions [here](../README.md#installation).
## Caveats
**When using `bat` as the printer:**
- Syntax highlighting in diffs between two files is not supported.
- Syntax highlighting in a single-file diff requires `bat` >= 0.15.
**When using `delta` as the printer:**
- The `--no-color` option does not remove all color output.
## Issues?
If you find an issue or have a feature suggestion, make a pull request or issue through GitHub!
Contributions are always welcome.

View file

@ -6,34 +6,34 @@ Search through files or directories looking for matching regular expressions (or
## Usage
## Command Line
batgrep [OPTIONS] PATTERN [PATH...]
**Synopsis:**
- `batgrep [OPTIONS] PATTERN [PATH...] `
## Options
**Options:**
| Short | Long | Description |
| ----- | ---------------------------- | ------------------------------------------------------------ |
| `-i` | `--ignore-case` | Use case insensitive searching. |
| `-s` | `--case-sensitive` | Use case sensitive searching. |
| `-S` | `--smart-case` | Use smart case searching. |
| `-A` | `--after-context=[LINES]` | Display the next *n* lines after a matched line. |
| `-B` | `--before-context=[LINES]` | Display the previous `n` lines before a matched line. |
| `-C` | `--context=[LINES]` | A combination of `--after-context` and `--before-context`. |
| `-p` | `--search-pattern` | Tell pager to search for `PATTERN`. Currently supported pagers: `less`. |
| | `--no-follow` | Do not follow symlinks. |
| | `--no-snip` | Do not show the `snip` decoration.<br /><br />This is automatically enabled when `--context=0` or when `bat --version` is less than `0.12.x`. |
| | `--no-highlight` | Do not highlight matching lines.<br /><br />This is automatically enabled when `--context=0`. |
| | `--color` | Force color output. |
| | `--no-color` | Force disable color output. |
| | `--paging=["never"/"always"]`| Enable/disable paging. |
| | `--pager=[PAGER]` | Specify the pager to use. |
| | `--terminal-width=[COLS]` | Generate output for the specified terminal width. |
| | `--no-separator` | Disable printing separator between files. |
| | `--rga` | Use `ripgrep-all` instead of `ripgrep`. |
| Short | Long | Description |
| ----- | -------------------------- | ------------------------------------------------------------ |
| `-i` | `--ignore-case` | Use case insensitive searching. |
| `-s` | `--case-sensitive` | Use case sensitive searching. |
| `-S` | `--smart-case` | Use smart case searching. |
| `-A` | `--after-context=[LINES]` | Display the next *n* lines after a matched line. |
| `-B` | `--before-context=[LINES]` | Display the previous `n` lines before a matched line. |
| `-C` | `--context=[LINES]` | A combination of `--after-context` and `--before-context`. |
| `-p` | `--search-pattern` | Tell pager to search for `PATTERN`. Currently supported pagers: `less`. |
| | `--no-follow` | Do not follow symlinks. |
| | `--no-snip` | Do not show the `snip` decoration.<br /><br />This is automatically enabled when `--context=0` or when `bat --version` is less than `0.12.x`. |
| | `--no-highlight` | Do not highlight matching lines.<br /><br />This is automatically enabled when `--context=0`. |
| | `--color` | Force color output. |
| | `--no-color` | Force disable color output. |
**Options (Passthrough)**:
The following options are passed directly to ripgrep, and are not handled by this script.
| Short | Long | Notes |

View file

@ -4,28 +4,17 @@ Read system manual pages (`man`) using `bat` as the manual page formatter.
Gone are the days of losing your place while reading through monotone manual pages. With `bat` and `batman`, you can read `man ifconfig` with beautiful 24-bit color and syntax higlighting.
If you have `fzf` installed, you can even use `batman` to search through manual pages!
## Usage
## Command Line
batman [SECTION] [ENTRY]
**Synopsis:**
### As a Replacement for Man
- `batman [SECTION] [ENTRY] `
With bash:
```bash
eval "$(batman --export-env)"
```
With fish:
```fish
batman --export-env | source
```
## Environment
**Environment:**
| Variable | Description |
| ---------- | ------------------------------------------------------------ |
@ -33,19 +22,12 @@ batman --export-env | source
## Customization
### Changing the Theme
You can change the syntax highlighting theme for `batman` by setting the `BAT_THEME` environment variable before calling `batman`. The following wrapper function will change the theme to `Solarized (dark)` without affecting any other `bat` command.
```bash
batman() {
BAT_THEME="Solarized (dark)" batman "$@"
return $?
}
```
## Caveats
None so far.
@ -55,18 +37,6 @@ This script is a part of the `bat-extras` suite of scripts. You can find install
## Caveats
**Flags aren't highlighted:**
- This happens when you change `bat`'s theme through `bat`'s config file or the `BAT_THEME` environment variable. Not all themes provide colours for flags, and [it's a known issue](https://github.com/sharkdp/bat/issues/2115).
- You can overriding the theme for `batman` by wrapping it in a function that sets `BAT_THEME`.
- The following themes support manpage highlighting:
- `Monokai Extended` / ``Monokai Extended Light`
- `Solarized (dark)` / `Solarized (light)`
## Acknowledgements
Thanks to [@sharkdp](https://github.com/sharkdp) and [@LunarLambda](https://github.com/LunarLambda) for debugging how to make this work properly in [certain environments](https://github.com/sharkdp/bat/issues/652).
@ -76,4 +46,4 @@ Thanks to [@sharkdp](https://github.com/sharkdp) and [@LunarLambda](https://gith
## Issues?
If you find an issue or have a feature suggestion, make a pull request or issue through GitHub!
Contributions are always welcome.
Contributions are always welcome.

View file

@ -1,83 +0,0 @@
# bat-extras: batpipe
A `less` (and `bat`) preprocessor for viewing more types of files in the terminal.
## Usage
Like [lesspipe](https://github.com/wofr06/lesspipe), `batpipe` is designed to work with programs that support preprocessing with the `LESSOPEN` environment variable. Setting up `batpipe` will depend on your shell:
**Bash:**
eval "$(batpipe)"
**Fish:**
eval (batpipe)
## Environment
| Variable | Description |
| -------------------- | ------------------------------------------------------------ |
| `BATPIPE_TERM_WIDTH` | Sets the terminal width provided to `bat`. If this variable starts with a hyphen (`-`), the number provided will be relative to the detected terminal size. |
## Built-in Viewers
| Files | Program |
| -------------------- | --------------------------- |
| Directories | `eza`, `ls` |
| `*.tar`, `*.tar.gz` | `tar` |
| `*.zip`, `*.jar` | `unzip` |
| `*.gz` | `gunzip` |
| `*.xz` | `xz` |
## External Viewers
For file formats that aren't supported by default, an external file viewer can be added to `batpipe` through the external viewer API.
External viewers are be added to batpipe by creating bash scripts inside the `~/.config/batpipe/viewers.d/` directory.
### Creating Viewers
Viewers must define two functions and append the viewer's name to the `$BATPIPE_VIEWERS` array.
- `viewer_${viewer}_supports [file_basename] [file_path] [inner_file_path]`
- `viewer_${viewer}_process [file_path] [inner_file_path]`
The `viewer_${viewer}_supports` function is called to determine if the external viewer is capable of viewing the provided file. If this function returns successfully, the corresponding `process` function will be called.
### API
$BATPIPE_VIEWERS -- An array of loaded file viewers.
$BATPIPE_ENABLE_COLOR -- Whether color is supported. (`true`|`false`)
$BATPIPE_INSIDE_LESS -- Whether batpipe is inside less. (`true`|`false`)
batpipe_header [pattern] [...] -- Print a viewer header line.
batpipe_subheader [pattern] [...] -- Print a viewer subheader line.
strip_trailing_slashes [path] -- Strips trailing slashes from a path.
## Caveats
- By default, `batpipe` will not use colors when previewed inside `less`.
Colors must be explicitly enabled with `BATPIPE=color`.
## Installation
This script is a part of the `bat-extras` suite of scripts. You can find install instructions [here](../README.md#installation).
## Issues?
If you find an issue or have a feature suggestion, make a pull request or issue through GitHub!
Contributions are always welcome.

View file

@ -1,30 +1,32 @@
# bat-extras: batwatch
Watch for changes in files or command output, and print them with `bat`.
Watch for changes in one or more files, and print them with `bat`.
## Usage
batwatch [OPTIONS] FILE...
batwatch [OPTIONS] --command COMMAND [ARG...]
## Command Line
**Synopsis:**
- `batwatch [OPTIONS] FILE... `
## Options
**Options:**
| Short | Long | Description |
| ----- | ---------------------- | ------------------------------------------------------------ |
| | `--command`\|`-x` | Execute a command on an interval. |
| | `--file`\|`-f` | Watch a file for changes. |
| | `--watcher=[watcher]` | Use a specific program to watch for file changes. See [below](#watchers) for more details. |
| | `--interval=[seconds]` | The interval for executing commands. |
| | `--clear` | Clear the screen before printing the files.<br />This is enabled by default. |
| | `--no-clear` | Do not clear the screen before printing the files. |
| | `--color` | Force color output. |
| | `--no-color` | Force disable color output. |
| Short | Long | Description |
| ----- | --------------------- | ------------------------------------------------------------ |
| | `--watcher=[watcher]` | Use a specific program to watch for file changes. See [below](#watchers) for more details. |
| | `--clear` | Clear the screen before printing the files.<br />This is enabled by default. |
| | `--no-clear` | Do not clear the screen before printing the files. |
| | `--color` | Force color output. |
| | `--no-color` | Force disable color output. |
**Options (Passthrough)**:
All remaining options are passed through to bat.
@ -34,7 +36,7 @@ All remaining options are passed through to bat.
Batwatch uses external programs to watch for file changes.
Currently, the following programs are supported:
- [entr](https://eradman.com/entrproject/)
- [entr](http://entrproject.org/)
There is also a fallback `poll` watcher available.
@ -46,8 +48,13 @@ This script is a part of the `bat-extras` suite of scripts. You can find install
## Caveats
None so far.
## Issues?
If you find an issue or have a feature suggestion, make a pull request or issue through GitHub!
Contributions are always welcome.
Contributions are always welcome.

View file

@ -6,14 +6,15 @@ The only thing better than syntax highlighting? Syntax highlighting *and* code f
## Usage
## Command Line
prettybat [OPTIONS] [PATH...]
**Synopsis:**
- `prettybat [OPTIONS] [PATH...] `
## Options
**Options:**
Every option is passed through to `bat`.
See `man bat` for more information.
@ -21,23 +22,20 @@ See `man bat` for more information.
## Languages
| Language | Formatter |
| -------------------- | ------------------------------------------------------------------------------------------------------ |
| JavaScript (JS, JSX) | [prettier](https://prettier.io/) |
| TypeScript (TS, TSX) | [prettier](https://prettier.io/) |
| CSS, SCSS, SASS | [prettier](https://prettier.io/) |
| Markdown | [prettier](https://prettier.io/) |
| JSON | [yq](https://mikefarah.gitbook.io/yq), [prettier](https://prettier.io/) |
| YAML | [yq](https://mikefarah.gitbook.io/yq), [prettier](https://prettier.io/) |
| HTML | [prettier](https://prettier.io/) |
| SVG | [prettier](https://prettier.io/) |
| Rust | [rustfmt](https://github.com/rust-lang/rustfmt) |
| Bash | [shfmt](https://github.com/mvdan/sh) |
| C | [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) |
| C++ | [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) |
| Objective-C | [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) |
| Python | [black](https://black.readthedocs.io/) |
| Elixir | [mix format](https://hexdocs.pm/mix/main/Mix.Tasks.Format.html) |
| Language | Formatter |
| -------------------- | ----------------------------------------------------------- |
| JavaScript (JS, JSX) | [prettier](https://prettier.io/) |
| TypeScript (TS, TSX) | [prettier](https://prettier.io/) |
| CSS, SCSS, SASS | [prettier](https://prettier.io/) |
| Markdown | [prettier](https://prettier.io/) |
| JSON | [prettier](https://prettier.io/) |
| YAML | [prettier](https://prettier.io/) |
| HTML | [prettier](https://prettier.io/) |
| Rust | [rustfmt](https://github.com/rust-lang/rustfmt) |
| Bash | [shfmt](https://github.com/mvdan/sh) |
| C | [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) |
| C++ | [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) |
| Objective-C | [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) |
@ -45,10 +43,11 @@ See `man bat` for more information.
## Caveats
- The header displayed by bat will show `STDIN` instead of the filename (`bat 0.13` or older).
- The header displayed by bat will show `STDIN` instead of the filename.
- The git changes sidebar will not work with files that have been formatted.
## Installation
This script is a part of the `bat-extras` suite of scripts. You can find install instructions [here](../README.md#installation).
@ -59,3 +58,4 @@ This script is a part of the `bat-extras` suite of scripts. You can find install
If you find an issue or have a feature suggestion, make a pull request or issue through GitHub!
Contributions are always welcome.

View file

@ -1,27 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2020 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
# Checks that the file or directory exists.
# Arguments:
# 1 -- The path to check.
check_exists() {
[[ -e "$1" ]] && return 0
print_error "%s: No such file or directory" "$1"
return 1
}
# Checks that the file is a file.
# Arguments:
# 1 -- The path to check.
check_is_file() {
[[ -f "$1" ]] && return 0
print_error "%s: Not a file" "$1"
return 1
}

View file

@ -1,30 +1,18 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2021 eth-p | MIT License
# 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
# -----------------------------------------------------------------------------
if [[ -z "$__LIB_CONSTANTS_INITIALIZED" ]]; then
__LIB_CONSTANTS_INITIALIZED=true
# Constants: Executables
EXECUTABLE_BAT="$(command -v bat 2>/dev/null || command -v batcat 2>/dev/null || echo "bat")"
EXECUTABLE_GIT="git"
EXECUTABLE_DELTA="delta"
EXECUTABLE_RIPGREP="rg"
EXECUTABLE_FZF="fzf"
# Constants: Program
EXECUTABLE_BAT="bat"
PROGRAM="$(basename "$0" .sh)"
PROGRAM_HOMEPAGE="https://github.com/eth-p/bat-extras"
PROGRAM_COPYRIGHT="Copyright (C) 2019-2021 eth-p | MIT License"
PROGRAM_COPYRIGHT="Copyright (C) 2019-2020 eth-p | MIT License"
PROGRAM_VERSION="$({
TOP="$(dirname "$(dirname "${BASH_SOURCE[0]}")")"
printf "%s" "$(cat "${TOP}/version.txt" 2>/dev/null || echo "unknown")"
if [[ -e "${TOP}/.git" ]]; then
printf "%s-git (%s)" "" "$("${EXECUTABLE_GIT}" -C "${TOP}" rev-parse --short HEAD)"
printf "%s-git (%s)" "" "$(git -C "${TOP}" rev-parse --short HEAD)"
fi
})"
fi

View file

@ -1,18 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2021 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
# Gets a configuration directory for a command-line program.
# Arguments:
# 1 -- The program name.
config_dir() {
if [[ -n "${XDG_CONFIG_HOME+x}" ]]; then
echo "${XDG_CONFIG_HOME}/$1"
else
echo "${HOME}/.config/$1"
fi
}

View file

@ -1,172 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2020 eth-p | MIT License
#
# ReRSTARTitory: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
# Parses a DSL file.
#
# Arguments:
# 1 -- The DSL file.
dsl_parse_file() {
dsl_parse < "$1"
return $?
}
# Parses DSL data.
# This calls callback functions to handle the parsed data:
#
# Format:
# | command arg1 arg2
# | option arg1 arg2
#
# Callbacks:
# dsl_on_raw "$indent" "$line" -- Called after every line.
# dsl_on_command "$command" "$arg1" "$arg2" ... -- Called on command lines.
# dsl_on_command_commit -- Called after commands and their options.
# dsl_on_option "$option" "$arg1" "$arg2" ... -- Called on option lines.
#
# Variables:
# DSL_LINE_NUMBER -- The line number being parsed at the time of a callback.
# DSL_COMMAND -- The command being parsed at the time of a callback.
#
# Input:
# The DSL data to parse.
dsl_parse() {
local line
local line_raw
local line_fields
local indent
local command
DSL_LINE_NUMBER=0
DSL_COMMAND=''
while IFS='' read -r line_raw; do
((DSL_LINE_NUMBER++)) || true
# Parse the indentation.
# If the indentation is greater than zero, it's considered an option.
[[ "$line_raw" =~ ^( |[[:space:]]{2,}) ]] || true
indent="${BASH_REMATCH[1]}"
line="${line_raw:${#indent}}"
if [[ -n "$line" ]] && ! [[ "$line" =~ ^# ]]; then
# Parse the line items.
eval "$(dsl_parse_line <<< "$line")"
# Call the appropriate on_* function.
if [[ "${#indent}" -eq 0 ]]; then
if [[ -n "$DSL_COMMAND" ]]; then
dsl_on_command_commit
fi
DSL_COMMAND="${line_fields[0]}"
dsl_on_command "${line_fields[@]}"
else
dsl_on_option "${line_fields[@]}"
fi
fi
# Call the on_raw function.
# This function can be used to echo back a line to rewrite the file.
dsl_on_raw "$indent" "$line"
done
if [[ -n "$DSL_COMMAND" ]]; then
dsl_on_command_commit
fi
return 0
}
# Parses a line into fields.
# This parses fields with a bash-like command parameter syntax:
#
# "arg 1" "" arg3
#
# Input:
# The line to parse.
#
# Output:
# A series of bash statemtents that write the fields into an array named "line_fields".
dsl_parse_line() {
awk '
{
print "line_fields=()"
n=0
buffer=""
quoted=0
while ($0 != "") {
quoted_once=0
while ($0 != "") {
# Match " ", "\", or quote.
if (!match($0, /[\t \\"]/)) {
buffer=sprintf("%s%s", buffer, $0)
$0=""
break
}
# Extract the character and previous literal string.
buffer=sprintf("%s%s", buffer, substr($0, 0, RSTART - 1))
chr=substr($0, RSTART, RLENGTH)
$0=substr($0, RSTART + RLENGTH)
# Handle the matched character.
if (chr == "\\") {
buffer=sprintf("%s%s", buffer, substr($0, 0, 1))
$0=substr($0, 2)
continue
}
if (chr == "\"") {
quoted=!quoted
quoted_once=1
continue
}
if ((chr == " " || chr == "\t") && quoted) {
buffer=sprintf("%s ", buffer)
continue
}
break
}
# If the buffer is empty and it is not intentionally empty,
# it should not be considered a separate field.
if (buffer == "" && !quoted_once) {
continue
}
# Escape the parsed value.
sub(/"/, "\\\"", buffer)
sub(/\$/, "\\$", buffer)
# Print the parsed value.
print sprintf("line_fields[%s]=\"%s\"", n, buffer)
buffer=""
n=n+1
}
}
'
}
dsl_on_raw() {
# Stub
:
}
#
#dsl_on_command() {
# :
#}
#
#dsl_on_command_commit() {
# :
#}
#
#dsl_on_option() {
# :
#}

View file

@ -1,86 +1,19 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2019-2020 eth-p | MIT License
# 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
# -----------------------------------------------------------------------------
source "${LIB}/constants.sh"
# An array of functions to call before returning from `shiftopt`.
#
# If one of these functions returns a successful exit code, the
# option will be transparently skipped instead of handled.
SHIFTOPT_HOOKS=()
# A setting to change how `shiftopt` will interpret short options that consist
# of more than one character.
#
# Values:
#
# SPLIT -- Splits the option into multiple single-character short options.
# "-abc" -> ("-a" "-b" "-c")
#
# VALUE -- Uses the remaining characters as the value for the short option.
# "-abc" -> ("-a=bc")
#
# CONV -- Converts the argument to a long option.
# "-abc" -> ("--abc")
#
# PASS -- Pass the argument along as-is.
# "-abc" -> ("-abc")
#
SHIFTOPT_SHORT_OPTIONS="VALUE"
# Sets the internal _ARGV, _ARGV_INDEX, and _ARGV_LAST variables used when
# parsing options with the shiftopt and shiftval functions.
#
# Arguments:
# ... -- The program arguments.
#
# Example:
# setargs "--long=3" "file.txt"
setargs() {
_ARGV=("$@")
_ARGV_LAST="$((${#_ARGV[@]} - 1))"
_ARGV_INDEX=0
_ARGV_SUBINDEX=1
}
# Gets all the remaining unparsed arguments and saves them to a variable.
#
# Arguments:
# "-a" -- Append the arguments to the variable instead of replacing it.
# $1 -- The variable to save the args to.
#
# Example:
# getargs remaining_args
getargs() {
if [[ "$1" = "-a" || "$1" = "--append" ]]; then
if [[ "${_ARGV_INDEX}" -ne "$((_ARGV_LAST+1))" ]]; then
eval "$2=(\"\${$2[@]}\" $(printf '%q ' "${_ARGV[@]:$_ARGV_INDEX}"))"
fi
else
if [[ "${_ARGV_INDEX}" -ne "$((_ARGV_LAST+1))" ]]; then
eval "$1=($(printf '%q ' "${_ARGV[@]:$_ARGV_INDEX}"))"
else
eval "$1=()"
fi
fi
}
# Resets the internal _ARGV* variables to the original script arguments.
# This is the equivalent of storing the top-level $@ and using setargs with it.
resetargs() {
setargs "${_ARGV_ORIGINAL[@]}"
}
# INTERNAL.
#
# Increments the argv index pointer used by `shiftopt`.
_shiftopt_next() {
_ARGV_SUBINDEX=1
((_ARGV_INDEX++)) || true
}
# Gets the next option passed to the script.
@ -103,47 +36,13 @@ shiftopt() {
OPT="${_ARGV[$_ARGV_INDEX]}"
unset OPT_VAL
if [[ "$OPT" =~ ^-[a-zA-Z0-9_-]+=.* ]]; then
if [[ "$OPT" =~ ^--[a-zA-Z0-9_-]+=.* ]]; then
OPT_VAL="${OPT#*=}"
OPT="${OPT%%=*}"
fi
# Handle short options.
if [[ "$OPT" =~ ^-[^-]{2,} ]]; then
case "$SHIFTOPT_SHORT_OPTIONS" in
# PASS mode: "-abc=0" -> ("-abc=0")
PASS) _shiftopt_next ;;
# CONV mode: "-abc=0" -> ("--abc=0")
CONV) OPT="-${OPT}"; _shiftopt_next ;;
# VALUE mode: "-abc=0" -> ("-a=bc=0")
VALUE) {
OPT="${_ARGV[$_ARGV_INDEX]}"
OPT_VAL="${OPT:2}"
OPT="${OPT:0:2}"
_shiftopt_next
} ;;
# SPLIT mode: "-abc=0" -> ("-a=0" "-b=0" "-c=0")
SPLIT) {
OPT="-${OPT:$_ARGV_SUBINDEX:1}"
((_ARGV_SUBINDEX++)) || true
if [[ "$_ARGV_SUBINDEX" -gt "${#OPT}" ]]; then
_shiftopt_next
fi
} ;;
# ????? mode: Treat it as pass.
*)
printf "shiftopt: unknown SHIFTOPT_SHORT_OPTIONS mode '%s'" \
"$SHIFTOPT_SHORT_OPTIONS" 1>&2
_shiftopt_next
;;
esac
else
_shiftopt_next
fi
# Pop array.
((_ARGV_INDEX++))
# Handle hooks.
local hook
@ -170,15 +69,9 @@ shiftval() {
if [[ -n "${OPT_VAL+x}" ]]; then
return 0
fi
if [[ "$_ARGV_SUBINDEX" -gt 1 && "$SHIFTOPT_SHORT_OPTIONS" = "SPLIT" ]]; then
# If it's a short group argument in SPLIT mode, we grab the next argument.
OPT_VAL="${_ARGV[$((_ARGV_INDEX+1))]}"
else
# Otherwise, we can handle it normally.
OPT_VAL="${_ARGV[$_ARGV_INDEX]}"
_shiftopt_next
fi
OPT_VAL="${_ARGV[$_ARGV_INDEX]}"
((_ARGV_INDEX++))
# Error if no value is provided.
if [[ "$OPT_VAL" =~ -.* ]]; then
@ -189,4 +82,3 @@ shiftval() {
# -----------------------------------------------------------------------------
setargs "$@"
_ARGV_ORIGINAL=("$@")

View file

@ -1,32 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2019-2020 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
# Option parser hook: --help support.
# This will accept -h or --help, which prints the usage information and exits.
hook_help() {
SHIFTOPT_HOOKS+=("__shiftopt_hook__help")
if [[ "$1" == "--no-short" ]]; then
__shiftopt_hook__help() {
if [[ "$OPT" = "--help" ]]; then
show_help
exit 0
fi
return 1
}
else
__shiftopt_hook__help() {
if [[ "$OPT" = "--help" ]] || [[ "$OPT" = "-h" ]]; then
show_help
exit 0
fi
return 1
}
fi
}

View file

@ -1,28 +0,0 @@
#!/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
# -----------------------------------------------------------------------------
source "${LIB}/term.sh"
# Option parser hook: --terminal-width support.
# This will accept --terminal-width=number.
#
# The variable OPT_TERMINAL_WIDTH will be set.
hook_width() {
SHIFTOPT_HOOKS+=("__shiftopt_hook__width")
__shiftopt_hook__width() {
case "$OPT" in
--terminal-width) shiftval; OPT_TERMINAL_WIDTH="$OPT_VAL" ;;
*) return 1 ;;
esac
return 0
}
# Default terminal width.
OPT_TERMINAL_WIDTH="$(term_width)"
}

View file

@ -1,6 +1,10 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2020-2024 eth-p and contributors | MIT License
# 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
# -----------------------------------------------------------------------------
source "${LIB}/opt_hook_color.sh"
source "${LIB}/opt_hook_pager.sh"
source "${LIB}/opt_hook_version.sh"

View file

@ -12,12 +12,6 @@ is_pager_less() {
return $?
}
# Returns 0 (true) if the current pager is bat, otherwise 1 (false).
is_pager_bat() {
[[ "$(pager_name)" = "bat" ]]
return $?
}
# Returns 0 (true) if the current pager is disabled, otherwise 1 (false).
is_pager_disabled() {
[[ -z "$(pager_name)" ]]
@ -26,13 +20,13 @@ is_pager_disabled() {
# Prints the detected pager name.
pager_name() {
_detect_pager 1>&2
_detect_pager
echo "$_SCRIPT_PAGER_NAME"
}
# Prints the detected pager version.
pager_version() {
_detect_pager 1>&2
_detect_pager
echo "$_SCRIPT_PAGER_VERSION"
}
@ -42,12 +36,14 @@ pager_version() {
# Example:
# pager_exec echo hi
pager_exec() {
if [[ -n "$SCRIPT_PAGER_CMD" ]]; then
"$@" | pager_display
return $?
else
"$@"
return $?
if [[ -n "$1" ]]; then
if [[ -n "$SCRIPT_PAGER_CMD" ]]; then
"$@" | "${SCRIPT_PAGER_CMD[@]}" "${SCRIPT_PAGER_ARGS[@]}"
return $?
else
"$@"
return $?
fi
fi
}
@ -57,16 +53,9 @@ pager_exec() {
# bat | pager_display
pager_display() {
if [[ -n "$SCRIPT_PAGER_CMD" ]]; then
if [[ -n "$SCRIPT_PAGER_ARGS" ]]; then
"${SCRIPT_PAGER_CMD[@]}" "${SCRIPT_PAGER_ARGS[@]}"
return $?
else
"${SCRIPT_PAGER_CMD[@]}"
return $?
fi
"${SCRIPT_PAGER_CMD[@]}" "${SCRIPT_PAGER_ARGS[@]}"
else
cat
return $?
fi
}
@ -75,7 +64,7 @@ pager_display() {
# Detect the pager information.
# shellcheck disable=SC2120
_detect_pager() {
if [[ "$_SCRIPT_PAGER_DETECTED" = "true" ]]; then return; fi
if [[ "$_SCRIPT_PAGER_DETECTED" = "true" && "$1" != "--force" ]]; then return; fi
_SCRIPT_PAGER_DETECTED=true
# If the pager command is empty, the pager is disabled.
@ -92,13 +81,9 @@ _detect_pager() {
output1="$(head -n 1 <<<"$output")"
if [[ "$output1" =~ ^less[[:blank:]]([[:digit:]]+) ]]; then
# shellcheck disable=SC2001
_SCRIPT_PAGER_VERSION="${BASH_REMATCH[1]}"
_SCRIPT_PAGER_NAME="less"
elif [[ "$output1" =~ ^bat(cat)?[[:blank:]]([[:digit:]]+) ]]; then
# shellcheck disable=SC2034
__BAT_VERSION="${BASH_REMATCH[2]}"
_SCRIPT_PAGER_VERSION="${BASH_REMATCH[2]}"
_SCRIPT_PAGER_NAME="bat"
else
_SCRIPT_PAGER_VERSION=0
_SCRIPT_PAGER_NAME="$(basename "${SCRIPT_PAGER_CMD[0]}")"
@ -112,15 +97,10 @@ _detect_pager() {
# 2. Use PAGER with special arguments for less
# 3. Use PAGER
_configure_pager() {
# shellcheck disable=SC2206
SCRIPT_PAGER_CMD=("$PAGER")
SCRIPT_PAGER_ARGS=()
if [[ -n "${PAGER+x}" ]]; then
SCRIPT_PAGER_CMD=($PAGER)
else
SCRIPT_PAGER_CMD=("less")
fi
# Prefer the BAT_PAGER environment variable.
# Prefer the bat pager.
if [[ -n "${BAT_PAGER+x}" ]]; then
# [note]: This is intentional.
# shellcheck disable=SC2206
@ -128,18 +108,12 @@ _configure_pager() {
SCRIPT_PAGER_ARGS=()
return
fi
# If the pager is bat, use less instead.
if is_pager_bat; then
SCRIPT_PAGER_CMD=("less")
SCRIPT_PAGER_ARGS=()
fi
# Add arguments for the less pager.
if is_pager_less; then
SCRIPT_PAGER_CMD=("${SCRIPT_PAGER_CMD[0]}" -R --quit-if-one-screen)
SCRIPT_PAGER_ARGS=(-R --quit-if-one-screen)
if [[ "$(pager_version)" -lt 500 ]]; then
SCRIPT_PAGER_CMD+=(--no-init)
SCRIPT_PAGER_ARGS+=(--no-init)
fi
fi
}

View file

@ -1,26 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2021 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
# Gets the file extension from a file path.
# Arguments:
# 1 -- The file path.
extname() {
local file="$1"
echo ".${file##*.}"
}
# Strips trailing slashes from a file path.
# Arguments:
# 1 -- The file path.
strip_trailing_slashes() {
local file="$1"
while [[ -n "$file" && "${file: -1}" = "/" ]]; do
file="${file:0:$((${#file}-1))}"
done
echo "$file"
}

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2021 eth-p | MIT License
# 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
@ -44,7 +44,7 @@ printc_init() {
_PRINTC_PATTERN_ANSI="${_PRINTC_PATTERN_ANSI}s/%{${name}}/${ansi}/g;"
done
if [[ -t 1 && -z "${NO_COLOR+x}" ]]; then
if [ -t 1 ]; then
_PRINTC_PATTERN="$_PRINTC_PATTERN_ANSI"
else
_PRINTC_PATTERN="$_PRINTC_PATTERN_PLAIN"
@ -81,6 +81,5 @@ printc_init "[DEFINE]" <<END
MAGENTA \x1B[35m
CYAN \x1B[36m
DEFAULT \x1B[39m
DIM \x1B[2m
END

View file

@ -1,70 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2021 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
# Gets the path to the parent executable file.
# Arguments:
# 1 -- The target pid. If not provided, the script's parent is used.
parent_executable() {
local target_pid="${1:-$PPID}"
ps -f -p "$target_pid" | tail -n 1 | awk '{for(i=8;i<=NF;i++) printf $i" "; printf "\n"}'
}
# Gets the PID of the parent executable file.
# Arguments:
# 1 -- The target pid. If not provided, the script's parent is used.
parent_executable_pid() {
local target_pid="${1:-$PPID}"
ps -f -p "$target_pid" | tail -n 1 | awk '{print $3}'
}
# Gets the path to the parent login shell.
# Arguments:
# 1 -- The target pid. If not provided, the script's parent is used.
parent_shell() {
local target_pid="${1:-$PPID}"
local target_name
while true; do
{
read -r target_pid
read -r target_name
# If the parent process starts with a "-", it's a login shell.
if [[ "${target_name:0:1}" = "-" ]]; then
target_name="$(cut -f1 -d' ' <<< "${target_name:1}")"
break
fi
# If the parent process is one of:
# - `*sh`; or
# - `nu`
# Followed by "-l", it's probably a login shell.
if [[ "$target_name" =~ ^(.*sh|nu)\ .*-l ]]; then
target_name="$(cut -f1 -d' ' <<< "${target_name}")"
break
fi
# If the parent process is pid 0 (init), then we haven't found a parent shell.
# At this point, it's best to assume the shell is whatever is defined in $SHELL.
if [[ "$target_pid" -eq 0 ]]; then
target_name="$SHELL"
break
fi
} < <({
ps -f -p "$target_pid" \
| tail -n 1 \
| awk '{print $3; for(i=8;i<=NF;i++) printf $i" "; printf "\n"}'
})
done
# Ensure that the detected shell is an executable path.
if [[ -f "$target_name" ]]; then
echo "$target_name"
elif ! command -v "$target_name" 2>/dev/null; then
echo "$target_name" # It's not, but we have nothing else we can do here.
fi
}

View file

@ -1,28 +0,0 @@
# ----------------------------------------------------------------------------------------------------------------------
# bat-extras | Copyright (C) 2020 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# ----------------------------------------------------------------------------------------------------------------------
# Gets the width of the terminal.
# This will return 80 unless stdin is attached to the terminal.
#
# Returns:
# The terminal width, or 80 if there's no TTY.
#
term_width() {
# shellcheck disable=SC2155
local width="$({ stty size 2>/dev/null || echo "22 80"; } | cut -d ' ' -f2)"
if [[ "$width" -ne 0 ]]; then
echo "$width"
else
echo "80"
fi
return 0
}
# Clears the terminal using the ANSI escape sequences for erase screen and cursor absolute positioning.
term_clear() {
printf "\x1B[3J\x1B[2J\x1B[H"
}

View file

@ -8,11 +8,8 @@
# Gets the current bat version.
bat_version() {
if [[ -z "${__BAT_VERSION}" ]]; then
__BAT_VERSION="$(command "$EXECUTABLE_BAT" --version | cut -d ' ' -f 2)"
fi
echo "${__BAT_VERSION}"
"$EXECUTABLE_BAT" --version | cut -d ' ' -f 2
return
}
# Compares two version strings.

382
mdroff.sh
View file

@ -1,382 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2020 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
# shellcheck disable=SC2021
# shellcheck disable=SC2155
printf_msg() {
# shellcheck disable=SC2059
printf "$@" 1>&2
}
printf_err() {
# shellcheck disable=SC2059
printf "$@" 1>&2
}
# -----------------------------------------------------------------------------
# MDroff:
# -----------------------------------------------------------------------------
mdroff:emit:h1() {
printf '.TH "%s" 1\n' "$1"
}
mdroff:emit:h2() {
printf '.SH "%s"\n' "$1"
}
mdroff:emit:h3() {
printf '.SS "%s"\n' "$1"
}
mdroff:emit:h4() {
printf '.SS "%s"\n' "$1"
}
mdroff:emit:h5() {
printf '.SS "%s"\n' "$1"
}
mdroff:emit:line() {
if "$MDROFF_PARAGRAPH"; then
MDROFF_PARAGRAPH=false
printf ".P\n%s\n" "$1"
else
printf ".br\n%s\n" "$1"
fi
}
mdroff:emit:attr() {
printf '\\fR'
if "$MDROFF_ATTR_STRONG"; then
printf '\\fB'
fi
if "$MDROFF_ATTR_EMPHASIS"; then
printf '\\fI'
fi
if "$MDROFF_ATTR_CODE"; then
printf '\\fI'
fi
}
mdroff:emit:link() {
printf "%s" "$1"
}
# shellcheck disable=SC2034
mdroff:emit:table_start() {
printf ".TS\n"
printf "tab(|) box;\n"
# Print Header Alignment
local temp
for temp in "$@"; do printf "| cB "; done
printf "|\n"
# Print Separator
for temp in "$@"; do printf "| _ "; done
printf "|\n"
# Print Column Alignment
local cols=("$@")
printf "| "
printf "%s0 |1 " "${cols:0:$((${#@}-1))}"
printf "%s " "${cols[$((${#@}-1))]}"
printf "|.\n"
}
mdroff:emit:table_end() {
printf ".TE\n\n"
}
mdroff:emit:table_heading() {
local heading="$(printf "| %s " "$@")"
printf "%s\n" "${heading:1}"
# Prevent tbl warning.
for temp in "${@:2}"; do printf "|"; done
printf "\n"
# Start table data.
printf ".SP\n"
}
mdroff:emit:table_row() {
local row="$(printf "| %s " "$@")"
printf "%s\n" "${row:1}"
}
mdroff:emit:text() {
local text="$1"
text="${text//<br>/ }"
text="${text//<br\/>/ }"
text="${text//<br \/>/ }"
printf "%s" "$text"
}
mdroff:emit() {
local type="$1"
local data="$2"
if type "mdroff:rewrite:${type}" &>/dev/null; then
data="$("mdroff:rewrite:${type}" "${@:2}")"
fi
if type "mdroff:emit_hook:${type}" &>/dev/null; then
"mdroff:emit_hook:${type}" "$data" "${@:3}"
return
fi
"mdroff:emit:${type}" "$data" "${@:3}"
}
mdroff:trim_right() {
# shellcheck disable=SC2001
sed 's/[[:space:]]*$//' <<< "$1"
}
mdroff:trim() {
sed 's/^[[:space:]]*//; s/[[:space:]]*$//' <<< "$1"
}
mdroff:parseln() {
MDROFF_ATTR_STRONG=false
MDROFF_ATTR_EMPHASIS=false
MDROFF_ATTR_CODE=false
local buffer="$1"
local before
local found
local pos
while [[ "${#buffer}" -gt 0 ]]; do
[[ "$buffer" =~ \*{1,3}|\`|\[([^\]]+)\]\(([^\)]+)\) ]] || {
mdroff:emit text "$(mdroff:trim_right "$buffer")"
mdroff:emit text $'\n'
return
}
found="${BASH_REMATCH[0]}"
pos="$(awk -v search="$found" '{print index($0,search) - 1}' <<< "$buffer")"
before="${buffer:0:$pos}"
buffer="${buffer:$(($pos + ${#found}))}"
mdroff:emit text "$before"
case "$found" in
'***')
if "$MDROFF_ATTR_STRONG" && "$MDROFF_ATTR_EMPHASIS"; then
MDROFF_ATTR_STRONG=false
MDROFF_ATTR_EMPHASIS=false
else
MDROFF_ATTR_STRONG=true
MDROFF_ATTR_EMPHASIS=true
fi
mdroff:emit attr
;;
'**')
if "$MDROFF_ATTR_STRONG"; then
MDROFF_ATTR_STRONG=false
else
MDROFF_ATTR_STRONG=true
fi
mdroff:emit attr
;;
'*')
if "$MDROFF_ATTR_EMPHASIS"; then
MDROFF_ATTR_EMPHASIS=false
else
MDROFF_ATTR_EMPHASIS=true
fi
mdroff:emit attr
;;
'`')
if "$MDROFF_ATTR_CODE"; then
MDROFF_ATTR_CODE=false
else
MDROFF_ATTR_CODE=true
fi
mdroff:emit attr
;;
'['*)
mdroff:emit link "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
esac
done
}
mdroff() {
MDROFF_HEADING_LEVEL=0
MDROFF_HEADING=''
MDROFF_ATTR_STRONG=false
MDROFF_ATTR_EMPHASIS=false
MDROFF_ATTR_CODE=false
MDROFF_IN_TABLE=false
MDROFF_PARAGRAPH=false
MDROFF_TABLE_HEADER=()
local line
local empty=0
local empty_continue=0
while IFS='' read -r line; do
line="$(mdroff:parseln "$line")"
# Empty
if [[ "$line" =~ ^[[:space:]]*$ ]]; then
((empty_continue++)) || true
if "$MDROFF_IN_TABLE"; then
mdroff:emit table_end
fi
MDROFF_PARAGRAPH=true
MDROFF_TABLE_HEADER=()
MDROFF_IN_TABLE=false
continue
fi
empty="$empty_continue"
empty_continue=0
# Headings
if [[ "$line" =~ ^(#{1,})[[:space:]]{1,}(.*)$ ]]; then
local level="${#BASH_REMATCH[1]}"
local text="${BASH_REMATCH[2]}"
MDROFF_HEADING_LEVEL="$level"
MDROFF_HEADING="$text"
mdroff:emit "h${level}" "$text"
MDROFF_PARAGRAPH=true
continue
fi
# Tables (Partially Supported)
if [[ "$line" =~ ^[[:space:]]*\| ]]; then
local raw_cells=()
local cells=()
local table_cell
line="$(sed 's/^[[:space:]]*|//; s/|[[:space:]]*$//' <<< "$line")"
# shellcheck disable=SC2206
IFS='|' raw_cells=($line)
for table_cell in "${raw_cells[@]}"; do
cells+=("$(mdroff:trim "$table_cell")")
done
if [[ "${cells[0]}" =~ ^[[:space:]]*-+[[:space:]]*$ ]]; then
# Calculate the column alignments.
local table_alignments=()
local table_cell
for table_cell in "${cells[@]}"; do
case "$table_cell" in
:-*:)
table_alignments+=('c')
;;
:-*)
table_alignments+=('l')
;;
*-:)
table_alignments+=('r')
;;
*)
table_alignments+=('l') # Unknown, but let's assume left.
esac
done
# Emit the table start and table header.
mdroff:emit table_start "${table_alignments[@]}"
mdroff:emit table_heading "${MDROFF_TABLE_HEADER[@]}"
MDROFF_TABLE_HEADER=()
continue
fi
if ! "$MDROFF_IN_TABLE"; then
MDROFF_IN_TABLE=true
MDROFF_TABLE_HEADER=("${cells[@]}")
else
if [[ "${#MDROFF_TABLE_HEADER[@]}" -ne 0 ]]; then
mdroff:emit table_heading "${MDROFF_TABLE_HEADER[@]}"
MDROFF_TABLE_HEADER=()
fi
mdroff:emit table_row "${cells[@]}"
fi
MDROFF_PARAGRAPH=true
continue
fi
mdroff:emit line "$line"
done < <(sed 's/<br *\/?>//')
}
# -----------------------------------------------------------------------------
# bat-extras:
# -----------------------------------------------------------------------------
mdroff:rewrite:h1() {
emitted_name=false
emitted_description=false
sed 's/^bat-extras: //' <<< "$1" | tr '[[:lower:]]' '[[:upper:]]'
}
mdroff:emit_hook:h2() {
case "$MDROFF_HEADING" in
"Installation") return ;;
"Issues?") return ;;
esac
mdroff:emit:h2 "$(tr '[[:lower:]]' '[[:upper:]]' <<< "$1")"
}
mdroff:emit_hook:line() {
if [[ "$MDROFF_HEADING_LEVEL" = "1" ]] && [[ "$emitted_name" != true ]]; then
emitted_name=true
printf ".SH NAME\n%s - %s\n" "$(sed 's/^bat-extras: //' <<< "$MDROFF_HEADING" | tr '[[:upper:]]' '[[:lower:]]')" "$1"
printf ".SH DESCRIPTION\n"
return
fi
case "$MDROFF_HEADING" in
"Installation") return ;;
"Issues?") return ;;
esac
mdroff:emit:line "$@"
}
# -----------------------------------------------------------------------------
# Main:
# -----------------------------------------------------------------------------
if [[ "${#BASH_SOURCE[@]}" -eq 1 ]]; then
case "$1" in
"") mdroff ;;
*) {
if ! [ -f "$1" ]; then
printf_err "%s: cannot find or read file %s\n" "$0" "$1"
exit 1;
fi
mdroff < "$1"
} ;;
esac
fi

View file

@ -1,350 +1,143 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2019-2023 eth-p | MIT License
# 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
# -----------------------------------------------------------------------------
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/build.sh"
# -----------------------------------------------------------------------------
# Release-as-a-Library Functions:
# -----------------------------------------------------------------------------
# Prints the path to the git workspace.
if ! batextras:is_function_defined ::get_git_workspace; then
batextras:get_git_workspace() {
batextras:lazy_done "${FUNCNAME[0]}" \
"$(batextras:get_project_directory)"
}
fi
# Prints the commit of the latest-tagged version of bat-extras.
if ! batextras:is_function_defined ::get_current_commit; then
batextras:get_current_commit() {
batextras:lazy_done "${FUNCNAME[0]}" \
"$(batextras:git rev-parse HEAD)"
}
fi
# Prints the commit of the latest-tagged version of bat-extras.
if ! batextras:is_function_defined ::get_previous_tag_commit; then
batextras:get_previous_tag_commit() {
local latest_tag
local before_latest_tag
{
read -r latest_tag
read -r before_latest_tag
} < <(batextras:git rev-list --tags --max-count=2)
# If the latest commit is a tag, go to the one before that.
if [[ "$(batextras:get_current_commit)" = "$latest_tag" ]]; then
latest_tag="${before_latest_tag}"
fi
batextras:lazy_done "${FUNCNAME[0]}" "$latest_tag"
}
fi
# Prints the ref name of the latest-tagged version of bat-extras.
if ! batextras:is_function_defined ::get_previous_tag_name; then
batextras:get_previous_tag_name() {
batextras:lazy_done "${FUNCNAME[0]}" \
"$(batextras:git describe --tags --abbrev=0 "$(batextras:get_previous_tag_commit)")"
}
fi
# Returns the suffix for a day of the month.
#
# Arguments:
# 1 -- The day number.
#
# Output:
# The suffix.
batextras:day_suffix() {
case "$1" in
11 | 12 | 13) echo "th" ;;
*1) echo "st" ;;
*2) echo "nd" ;;
*3) echo "rd" ;;
*) echo "th" ;;
esac
}
# Runs `git` within the project directory.
#
# This takes the same arguments as git (with the exception of `-C`), and
# does exactly what `git` would normally do.
batextras:git() {
git -C "$(batextras:get_git_workspace)" "$@"
return $?
}
# Creates the zipball for release.
# YOU MUST BUILD THE PROJECT FIRST!
#
# Arguments:
# 1 -- The absolute path to the output zip file.
#
# Stderr:
# Messages.
batextras:create_package() {
local artifact="$1"
local bin_dir man_dir doc_dir
bin_dir="$(batextras:get_output_bin_directory)"
man_dir="$(batextras:get_output_man_directory)"
doc_dir="$(batextras:get_docs_directory)"
(
# Remove the old zipball, if one exists.
if [[ -f "$artifact" ]]; then
rm "$artifact" || return $?
fi
# Add the bin directory.
cd "$(dirname -- "$bin_dir")" || return $?
zip -r "$artifact" "$(basename -- "$bin_dir")"
# Add the doc directory.
cd "$(dirname -- "$doc_dir")" || return $?
zip -ru "$artifact" "$(basename -- "$doc_dir")"
# Add the man directory.
if [[ -d "$man_dir" ]]; then
cd "$(dirname -- "$man_dir")" || return $?
zip -ru "$artifact" "$(basename -- "$man_dir")"
fi
) 1>&2
}
# Generates a Markdown changelog for all changes between two commits.
#
# Arguments:
# 1 -- The first commit, exclusive.
# 2 -- The second commit, inclusive.
# 3 -- A filter in regex.
#
# Output:
# The changelog.
batextras:generate_changelog() {
local start_commit="$1"
local end_commit="$2"
local filter="${3}"
# Generate sed replacement patterns.
local script_links=()
local script_names=()
local script script_name
while read -r script; do
script_name="$(basename "$script" .sh)"
script_names+=("$script_name")
done < <(batextras:get_source_paths)
local script_pattern
script_pattern="$(printf 's/\\(%s\\)/`\\1`/;' "${script_names[@]}")"
# Generate the changelog.
local changelog=''
local commit
local affected_module
local commit_message
while read -r commit; do
commit_message="$(batextras:git show -s --format=%s "$commit")"
if ! [[ "$commit_message" =~ ^([a-z-]+):[[:space:]]*(.*)$ ]]; then
continue
fi
affected_module="${BASH_REMATCH[1]}"
# Make module names consistent.
case "$affected_module" in
dev | lib | mdroff) affected_module="developer" ;;
tests) affected_module="test" ;;
doc) affected_module="docs" ;;
esac
# Append to changelog.
if [[ "$affected_module" =~ ^($filter)$ ]]; then
changelog="$changelog"$'\n'" - ${commit_message}"
fi
done < <(batextras:git rev-list "${start_commit}..${end_commit}")
# Print the changelog.
changelog="$(sed "$script_pattern" <<< "$changelog")"
printf "%s\n" "${changelog:1}"
return 0
}
# Generates the Markdown release notes.
#
# Arguments:
# 1 -- The oldest commit, exclusive.
# 2 -- The newest commit, inclusive.
#
# Output:
# The changelog.
batextras:generate_release_notes() {
local commit_oldest="$1"
local commit_newest="$2"
local commit_newest_url="https://github.com/eth-p/bat-extras/tree/${commit_newest}"
local date_str
# Get the commit date.
local date_year date_month date_day date_month_text date_day_suffix
read -r date_year date_month date_day date_month_text \
< <(batextras:git show -s --format="%cd" --date="format:%Y %m %d %B" "$commit_newest")
date_day_suffix="$(batextras:day_suffix "$date_day")"
date_str="${date_month_text} ${date_day}${date_day_suffix}, ${date_year}"
# For each built script, we want to:
# - Get the name of the script.
# - Get a link to the documentation.
# - Add it to the filter for non-developer items.
local script_name script_names script_links script_filters script_list_markdown
script_links=()
script_names=()
script_filters=''
while read -r script; do
script_name="$(basename "$script" .sh)"
script_names+=("$script_name")
script_links+=("[\`${script_name}\`](https://github.com/eth-p/bat-extras/blob/${commit_newest}/doc/${script_name}.md)")
script_filters="${script_filters}|$(printf "%q" "$script_name")"
done < <(batextras:get_source_paths)
script_filters="${script_filters:1}" # Remove the leading "|"
script_list_markdown="$(printf "%s, " "${script_links[@]:0:$((${#script_links[@]} - 1))}")"
script_list_markdown="${script_list_markdown}and ${script_links[$((${#script_links[@]} - 1))]}"
# Get the changelog.
local changelog changelog_dev
changelog="$(batextras:generate_changelog "$commit_oldest" "$commit_newest" "$script_filters")"
changelog_dev="$(batextras:generate_changelog "$commit_oldest" "$commit_newest" "test|developer|ci|build")"
# Print the template.
{ sed '/\\$/{N;s/\\\n//;s/\n//p;}'; } <<- EOF
This contains the latest versions of ${script_list_markdown} as of commit [${commit_newest}](${commit_newest_url}) (${date_str}).
**This is provided as a convenience only.**
I would still recommend following the installation instructions in [the README](https://github.com/eth-p/bat-extras#installation-) for the most up-to-date versions.
### Changes
${changelog}
### Developer
<details>
<div markdown="1">
${changelog_dev}
</div>
</details>
EOF
}
# -----------------------------------------------------------------------------
# Main:
# Only run everything past this point if the script is not sourced.
# -----------------------------------------------------------------------------
(return 0 2>/dev/null) && return 0
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DATE="$(date +%Y%m%d)"
VERSION="$(< "${HERE}/version.txt")"
VERSION="$(<"${HERE}/version.txt")"
VERSION_EXPECTED="$(date +%Y.%m.%d)"
LIB="$HERE/lib"
SRC="$HERE/src"
source "${LIB}/print.sh"
source "${LIB}/opt.sh"
# -----------------------------------------------------------------------------
set -euo pipefail
# -----------------------------------------------------------------------------
# Options.
OPT_ARTIFACT="bat-extras-${DATE}.zip"
OPT_SINCE=
OPT_BAD_IDEA=false
OPT_BIN_DIR="$(batextras:get_output_bin_directory)"
OPT_DOC_DIR="$(batextras:get_docs_directory)"
OPT_MAN_DIR="$(batextras:get_output_man_directory)"
OPT_BIN_DIR="$HERE/bin"
OPT_DOC_DIR="$HERE/doc"
while shiftopt; do
case "$OPT" in
--since)
shiftval
OPT_SINCE="$OPT_VAL"
if ! batextras:git rev-parse "$OPT_SINCE" &> /dev/null; then
printc "%{RED}%s: unknown commit or tag for '%s'\n" "$PROGRAM" "$OPT"
exit 1
fi
;;
--badidea)
OPT_BAD_IDEA=true
;;
*)
printc "%{RED}%s: unknown option '%s'%{CLEAR}" "$PROGRAM" "$OPT"
--since)
shiftval
OPT_SINCE="$OPT_VAL"
if ! git rev-parse "$OPT_SINCE" &>/dev/null; then
printc "%{RED}%s: unknown commit or tag for '%s'\n" "$PROGRAM" "$OPT"
exit 1
;;
fi
;;
*)
printc "%{RED}%s: unknown option '%s'%{CLEAR}" "$PROGRAM" "$OPT"
exit 1
;;
esac
done
# -----------------------------------------------------------------------------
# Verify the version matches today's date.
VERSION="$(source "${LIB}/constants.sh" && echo "${PROGRAM_VERSION}")"
VERSION_EXPECTED="$(date +%Y.%m.%d)"
if [[ "$VERSION" != "$VERSION_EXPECTED" ]] && ! "$OPT_BAD_IDEA"; then
printc "%{RED}The expected version does not match %{DEFAULT}version.txt%{RED}!%{CLEAR}\n"
if [[ "$VERSION" != "$VERSION_EXPECTED" ]]; then
printc "%{RED}The expected version does not match %{WHITE}version.txt%{RED}!%{CLEAR}\n"
printc "%{RED}Expected: %{YELLOW}%s%{CLEAR}\n" "$VERSION_EXPECTED"
printc "%{RED}Actual: %{YELLOW}%s%{CLEAR}\n" "$VERSION"
exit 1
fi
# -----------------------------------------------------------------------------
# Verify the working tree is clean-ish.
if ! "$OPT_BAD_IDEA"; then
while read -r flags file; do
if [[ "$flags" =~ M ]]; then
printc "%{RED}Found an uncommitted change in %{DEFAULT}%s%{RED}!%{CLEAR}\n" "$file"
exit 1
fi
done < <(batextras:git status --porcelain)
fi
# -----------------------------------------------------------------------------
# Build files.
# Clean the old files.
# Make sure it's not trying to delete /bin or /man first, though.
if [[ "$OPT_BIN_DIR" != "/bin" ]]; then rm -rf "$OPT_BIN_DIR"; fi
if [[ "$OPT_MAN_DIR" != "/man" ]]; then rm -rf "$OPT_MAN_DIR"; fi
# Clean the old bin files.
# Make sure it's not trying to delete /bin first, though.
if [[ "$OPT_BIN_DIR" != "/bin" ]]; then
rm -rf "$OPT_BIN_DIR"
fi
# Generate the new bin files.
printc "%{YELLOW}Building scripts...%{CLEAR}\n"
"$HERE/build.sh" --minify=all --alternate-executable='bat' --no-inline &>/dev/null || {
"$HERE/build.sh" --minify=all &>/dev/null || {
printc "%{RED}FAILED TO BUILD SCRIPTS.%{CLEAR}\n"
printc "%{RED}CAN NOT PROCEED WITH RELEASE.%{CLEAR}\n"
exit 1
}
# -----------------------------------------------------------------------------
# Tag release.
if ! "$OPT_SKIP_TAG"; then
printc "%{YELLOW}Tagging release...%{CLEAR}\n"
git tag "$OPT_TAG"
fi
# -----------------------------------------------------------------------------
# Build package.
printc "%{YELLOW}Packaging artifacts...%{CLEAR}\n"
batextras:create_package "$OPT_ARTIFACT"
zip -r "$OPT_ARTIFACT" \
"$OPT_BIN_DIR" \
"$OPT_DOC_DIR"
printc "%{YELLOW}Package created as %{BLUE}%s%{YELLOW}.%{CLEAR}\n" "$OPT_ARTIFACT"
# -----------------------------------------------------------------------------
# Print template description package.
printc "%{YELLOW}Release description:%{CLEAR}\n"
batextras:generate_release_notes \
"$(batextras:get_previous_tag_name)" \
"$(batextras:get_current_commit)"
# Get the commit hash.
COMMIT="$(git rev-parse HEAD)"
# Get the release date string.
DATE_DAY="$(date +%e | sed 's/ //')"
DATE_SUFFIX=""
case "$DATE_DAY" in
11|12|13) DATE_SUFFIX="th" ;;
*1) DATE_SUFFIX="st" ;;
*2) DATE_SUFFIX="nd" ;;
*3) DATE_SUFFIX="rd" ;;
*) DATE_SUFFIX="th" ;;
esac
DATE_STR="$(date +'%B') ${DATE_DAY}${DATE_SUFFIX}, $(date +'%Y')"
# Get the changelog.
CHANGELOG=''
if [[ -n "$OPT_SINCE" ]]; then
ref="$(git rev-parse HEAD)"
end="$(git rev-parse "$OPT_SINCE")"
while [[ "$ref" != "$end" ]]; do
ref_message="$(git show -s --format=%s "$ref")"
ref="$(git rev-parse "${ref}~1")"
if [[ "$ref_message" =~ ^([a-z]):[[:space:]]*(.*)$ ]]; then
ref_message="\`${BASH_REMATCH[1]}\`: ${BASH_REMATCH[2]}"
fi
# Append to changelog.
if [[ -z "$CHANGELOG" ]]; then
CHANGELOG=" - ${ref_message}"
else
CHANGELOG="$CHANGELOG"$'\n'" - ${ref_message}"
fi
done
fi
# Print the template.
sed '/\\$/{N;s/\\\n//;s/\n//p;}' <<-EOF
This contains the latest versions of \
[\`batgrep\`](https://github.com/eth-p/bat-extras/blob/${COMMIT}/doc/batgrep.md), \
[\`batman\`](https://github.com/eth-p/bat-extras/blob/${COMMIT}/doc/batman.md), \
[\`batwatch\`](https://github.com/eth-p/bat-extras/blob/${COMMIT}/doc/batwatch.md), and \
[\`prettybat\`](https://github.com/eth-p/bat-extras/blob/${COMMIT}/doc/prettybat.md) \
as of commit $(git rev-parse --short HEAD) (${DATE_STR}).
**This is provided as a convenience only.**
I would still recommend following the installation instructions in \
[the README](https://github.com/eth-p/bat-extras#installation-) for the most up-to-date versions.
### Changes
$CHANGELOG
EOF

View file

@ -1,251 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2020 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)"
source "${LIB}/constants.sh"
source "${LIB}/print.sh"
source "${LIB}/opt.sh"
source "${LIB}/opt_hook_color.sh"
source "${LIB}/opt_hook_version.sh"
source "${LIB}/dsl.sh"
source "${LIB}/str.sh"
# -----------------------------------------------------------------------------
# Init:
# -----------------------------------------------------------------------------
hook_color
hook_version
# -----------------------------------------------------------------------------
COMMON_URL_GITHUB="https://github.com/%s.git"
COMMON_URL_GITLAB="https://gitlab.com/%s.git"
CONFIG_DIR="$(bat --config-dir)"
SYNTAX_DIR="${CONFIG_DIR}/syntaxes"
THEME_DIR="${CONFIG_DIR}/themes"
MODULES_FILE="${CONFIG_DIR}/modules.txt"
# -----------------------------------------------------------------------------
# Options:
# -----------------------------------------------------------------------------
ACTION="help"
# Parse arguments.
while shiftopt; do
case "$OPT" in
--help) ACTION="help" ;;
--update) ACTION="update" ;;
--clear) ACTION="clear" ;;
--setup) ACTION="setup" ;;
--modules-file) ACTION="show_file" ;;
# ???
-*) {
printc "%{RED}%s: unknown option '%s'%{CLEAR}\n" "$PROGRAM" "$OPT" 1>&2
exit 1
} ;;
esac
done
# -----------------------------------------------------------------------------
# Functions:
# -----------------------------------------------------------------------------
# Ensures that the modules file at $MODULES_FILE exists.
# If it doesn't, this will print a friendly warning and exit with exit code 1.
#
# This will also make sure the syntaxes and themes directories exist.
ensure_setup() {
if ! [[ -f "$MODULES_FILE" ]]; then
printc "%{YELLOW}The bat-modules modules file wasn't found.%{CLEAR}\n"
printc "%{YELLOW}Use %{CLEAR}%s --setup%{YELLOW} to set up bat-modules, or%{CLEAR}\n" "${PROGRAM}"
printc "%{YELLOW}read the documentation at %{CLEAR}%s%{YELLOW} for more info.%{CLEAR}\n" "${PROGRAM_HOMEPAGE}"
exit 1
fi
mkdir -p "${SYNTAX_DIR}" &>/dev/null || true
mkdir -p "${THEME_DIR}" &>/dev/null || true
}
# Prints an error message that parsing
fail_parsing() {
print_warning "Failed to parse bat-modules file."
print_warning "Line %s: %s" "$DSL_LINE" "$1"
exit 1
}
# -----------------------------------------------------------------------------
# Parsing:
# -----------------------------------------------------------------------------
dsl_on_command() {
BM_TYPE="$(tolower "$1")"
BM_SOURCE="$(parse_source "$2")"
BM_OPT_CHECKOUT="master"
case "$BM_TYPE" in
"syntax" | "theme") : ;;
*) fail "unknown module type '$BM_TYPE'" ;;
esac
}
dsl_on_option() {
# Common options.
case "$(tolower "$1")" in
checkout)
BM_OPT_CHECKOUT="$2"
return 0 ;;
esac
# Type-specific options.
case "$BM_TYPE" in
"syntax") on_option_for_syntax "$@" && return 0 ;;
"theme") on_option_for_theme "$@" && return 0 ;;
esac
# Unknown options.
fail "unknown %s option '%s'" "$BM_TYPE" "$*"
}
on_option_for_syntax() {
:
}
on_option_for_theme() {
:
}
# Parses a module source.
# This takes a git url or pseudo-URL patterns such as:
#
# example/my-syntax-on-github
# github:example/my-syntax
# gitlab:example/my-syntax
#
# Arguments:
# 1 -- The source string.
parse_source() {
local source="$1"
# shellcheck disable=SC2059
case "$source" in
"github:"* | "gh:"*)
source="$(printf "$COMMON_URL_GITHUB" "$(cut -d':' -f2- <<< "$source")")"
;;
"gitlab:"* | "gl:"*)
source="$(printf "$COMMON_URL_GITLAB" "$(cut -d':' -f2- <<< "$source")")"
;;
*)
if [[ "$1" =~ ^([A-Za-z0-9-])+/([A-Za-z0-9-])+$ ]]; then
parse_source "github:$1" "${@:2}"
return $?
fi
;;
esac
echo "$source"
}
# Parses the clone directory name of a git repo URL.
# Arguments:
# 1 -- The repo URL.
parse_source_name() {
basename "$1" .git
}
# -----------------------------------------------------------------------------
# Actions:
# -----------------------------------------------------------------------------
action:show_file() {
printf "%s\n" "$MODULES_FILE"
}
action:setup() {
if ! [[ -f "$MODULES_FILE" ]]; then
cat > "$MODULES_FILE" <<-EOF
# bat-modules example file.
# See ${PROGRAM_HOMEPAGE} for documentation and help.
# syntax example/syntax
# theme https://github.com/example/theme.git
# checkout abcdef1
EOF
fi
"${EDITOR:-vi}" "$MODULES_FILE"
}
action:help() {
{
printc "%{YELLOW}%s help:%{CLEAR}\n" "$PROGRAM"
printc " --clear -- Clear the cached themes and syntaxes.\n"
printc " --update -- Update themes and syntaxes.\n"
printc " --setup -- Edit the bat-modules modules.txt file.\n"
printc " --modules-file -- Show the bat-modules modules.txt file.\n"
} 1>&2
}
action:clear() {
printc "%{YELLOW}Clearing bat syntax and theme cache...%{CLEAR}\n"
"$EXECUTABLE_BAT" cache --clear
}
action:update() {
CHANGES=false
dsl_on_command_commit() {
case "$BM_TYPE" in
syntax) cd "$SYNTAX_DIR" ;;
theme) cd "$THEME_DIR" ;;
esac
local hash
local name="$(parse_source_name "$BM_SOURCE")"
printc "%{BLUE}----- %s: %s -----%{CLEAR}\n" "$BM_TYPE" "$name"
# If it isn't cloned, clone it.
if ! [[ -d "$name" ]]; then
printc "%{YELLOW}Cloning...%{CLEAR}\n"
"$EXECUTABLE_GIT" clone "$BM_SOURCE" "$name"
CHANGES=true
fi
# If it is cloned, fetch/checkout.
printc "%{YELLOW}Updating...%{CLEAR}\n"
cd "$name"
hash="$("$EXECUTABLE_GIT" rev-parse HEAD)"
"$EXECUTABLE_GIT" fetch origin --quiet
"$EXECUTABLE_GIT" checkout "$BM_OPT_CHECKOUT" --quiet
hash_new="$("$EXECUTABLE_GIT" rev-parse HEAD)"
if [[ "$hash" != "$hash_new" ]]; then
printc "%{YELLOW}Updated to %s.%{CLEAR}\n" "$hash_new"
CHANGES=true
fi
}
# Parse the DSL.
ensure_setup
dsl_parse_file "$MODULES_FILE"
# If there are changes, update.
printc "%{BLUE}----- bat-modules -----%{CLEAR}\n" "$BM_TYPE" "$name"
printc "%{YELLOW}Done.%{CLEAR}\n"
if "$CHANGES"; then
printc "%{YELLOW}Rebuilding cache...%{CLEAR}\n"
"$EXECUTABLE_BAT" cache --build
fi
}
# -----------------------------------------------------------------------------
# Main:
# -----------------------------------------------------------------------------
action:"$ACTION"
exit $?

View file

@ -1,227 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2020 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)"
source "${LIB}/constants.sh"
source "${LIB}/print.sh"
source "${LIB}/pager.sh"
source "${LIB}/opt.sh"
source "${LIB}/opt_hook_color.sh"
source "${LIB}/opt_hook_pager.sh"
source "${LIB}/opt_hook_version.sh"
source "${LIB}/opt_hook_width.sh"
source "${LIB}/version.sh"
# -----------------------------------------------------------------------------
# Init:
# -----------------------------------------------------------------------------
hook_color
hook_pager
hook_version
hook_width
# -----------------------------------------------------------------------------
# Options:
# -----------------------------------------------------------------------------
BATDIFF_USE_DELTA="${BATDIFF_USE_DELTA:-}"
SUPPORTS_BAT_DIFF=false
SUPPORTS_DELTA=false
BAT_VERSION="$(bat_version)"
BAT_ARGS=()
DELTA_ARGS=()
DELTA_VERSION='unsupported'
GIT_ARGS=()
FILES=()
OPT_TABS=
OPT_CONTEXT=2
OPT_STAGED=false
OPT_ALL_CHANGES=false
# Set options based on bat version.
if version_compare "$BAT_VERSION" -ge "0.15"; then
SUPPORTS_BAT_DIFF=true
fi
# Set options based on delta availability.
if command -v "$EXECUTABLE_DELTA" &>/dev/null; then
SUPPORTS_DELTA=true
DELTA_VERSION="$("$EXECUTABLE_DELTA" --version | cut -d' ' -f2)"
fi
# Set options based on delta version.
# - 0.12 -- Renamed `--hunk-style` to `--hunk-header-decoration-style`.
if version_compare "$DELTA_VERSION" -ge "0.12"; then
DELTA_ARGS+=("--hunk-header-decoration-style=plain")
else
DELTA_ARGS+=("--hunk-style=plain")
fi
# Parse arguments.
while shiftopt; do
case "$OPT" in
# bat options
-C | --context | --diff-context) shiftval; OPT_CONTEXT="$OPT_VAL" ;;
--terminal-width) shiftval; OPT_TERMINAL_WIDTH="$OPT_VAL" ;;
--tabs) shiftval; OPT_TABS="$OPT_VAL" ;;
# Script options
--all) OPT_ALL_CHANGES=true ;;
--staged) OPT_STAGED=true; GIT_ARGS+=("--staged") ;;
--delta) BATDIFF_USE_DELTA=true ;;
# ???
-*) {
printc "%{RED}%s: unknown option '%s'%{CLEAR}\n" "$PROGRAM" "$OPT" 1>&2
exit 1
} ;;
# Files
*) FILES+=("$OPT") ;;
esac
done
# Append arguments for delta/bat.
BAT_ARGS+=("--terminal-width=${OPT_TERMINAL_WIDTH}" "--paging=never")
DELTA_ARGS+=(
"--width=${OPT_TERMINAL_WIDTH}"
"--paging=never"
)
if "$OPT_COLOR"; then
BAT_ARGS+=("--color=always")
else
BAT_ARGS+=("--color=never")
DELTA_ARGS+=("--theme=none")
fi
if [[ -n "$OPT_TABS" ]]; then
BAT_ARGS+=("--tabs=${OPT_TABS}")
DELTA_ARGS+=("--tabs=${OPT_TABS}")
fi
# Append arguments for git.
GIT_ARGS+=(-U"$OPT_CONTEXT")
# -----------------------------------------------------------------------------
# Functions:
# -----------------------------------------------------------------------------
ensure_git_directory() {
if ! "$EXECUTABLE_GIT" rev-parse --show-toplevel &>/dev/null; then
print_error "Not a git repository."
printc "\n%s\nTo view a diff between two files, use %{CYAN}%s [file] [file]%{CLEAR}." \
"To view a diff between staged files and the working directory, enter a git repository." \
"$PROGRAM"
exit 1
fi
}
# -----------------------------------------------------------------------------
# Printing:
# -----------------------------------------------------------------------------
print_bat_diff() {
local files=("$@")
# Diff two files.
if [[ "${#files[@]}" -eq 2 ]]; then
diff --unified="$OPT_CONTEXT" "${files[@]}" | "$EXECUTABLE_BAT" --language=diff - "${BAT_ARGS[@]}"
return $?
fi
# Diff staged git file.
ensure_git_directory
if "$OPT_STAGED"; then
if false && "$SUPPORTS_DELTA"; then
# bat doesn't support diffing staged changes against the HEAD.
# Delta is better suited for printing diffs in this case.
print_delta_diff "$@"
else
difftext="$("$EXECUTABLE_GIT" diff "${GIT_ARGS[@]}" "${files[0]}")"
if [[ "${#difftext}" -gt 0 ]]; then
"$EXECUTABLE_BAT" --language=diff --file-name="${files[0]}" - "${BAT_ARGS[@]}" <<< "$difftext"
fi
fi
return $?
fi
# Diff git file.
if "$SUPPORTS_BAT_DIFF"; then
"$EXECUTABLE_GIT" diff "${GIT_ARGS[@]}" --name-only -z "${files[0]}" \
| xargs --null "$EXECUTABLE_BAT" --diff --diff-context="$OPT_CONTEXT" "${BAT_ARGS[@]}"
else
"$EXECUTABLE_GIT" diff "${GIT_ARGS[@]}" "${files[0]}" | "$EXECUTABLE_BAT" --language=diff - "${BAT_ARGS[@]}"
fi
}
print_delta_diff() {
local files=("$@")
# Diff two files.
if [[ "${#files[@]}" -eq 2 ]]; then
diff --unified="$OPT_CONTEXT" "${files[@]}" | "$EXECUTABLE_DELTA" "${DELTA_ARGS[@]}"
return $?
fi
# Diff git file.
ensure_git_directory
"$EXECUTABLE_GIT" diff "${GIT_ARGS[@]}" "${files[0]}" | "$EXECUTABLE_DELTA" "${DELTA_ARGS[@]}"
}
if [[ "$BATDIFF_USE_DELTA" = "true" && "$SUPPORTS_DELTA" = "true" ]]; then
print_diff() {
print_delta_diff "$@"
return $?
}
else
print_diff() {
print_bat_diff "$@"
return $?
}
fi
# -----------------------------------------------------------------------------
# Validation:
# -----------------------------------------------------------------------------
# Handle too many files.
if [[ "${#FILES[@]}" -gt 2 ]]; then
print_error "too many files provided"
exit 1
fi
# Handle deprecated --all.
if "$OPT_ALL_CHANGES"; then
print_warning "argument --all is deprecated. Use '%s' instead" "$0"
fi
# -----------------------------------------------------------------------------
# Main:
# -----------------------------------------------------------------------------
main() {
if [[ "${#FILES[@]}" -eq 0 ]] || "$OPT_ALL_CHANGES"; then
ensure_git_directory
local file
while read -r file; do
if [[ -f "$file" ]]; then
print_diff "$file"
fi
done < <("${EXECUTABLE_GIT}" diff "${GIT_ARGS[@]}" --name-only --diff-filter=d)
return
fi
print_diff "${FILES[@]}"
}
pager_exec main
exit $?

View file

@ -5,146 +5,23 @@
# 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)"
source "${LIB}/constants.sh"
source "${LIB}/print.sh"
source "${LIB}/pager.sh"
source "${LIB}/opt.sh"
source "${LIB}/opt_hook_color.sh"
source "${LIB}/opt_hook_help.sh"
source "${LIB}/opt_hook_pager.sh"
source "${LIB}/opt_hook_version.sh"
source "${LIB}/opt_hook_width.sh"
source "${LIB}/opt_hooks.sh"
source "${LIB}/version.sh"
# -----------------------------------------------------------------------------
# Init:
# -----------------------------------------------------------------------------
hook_color
hook_help
hook_pager
hook_version
hook_width
# -----------------------------------------------------------------------------
# Help:
# -----------------------------------------------------------------------------
show_help() {
cat <<-'EOF'
Quickly search through and highlight files using ripgrep.
Search through files or directories looking for matching regular expressions (or fixed strings with -F), and print the output using bat for an easy and syntax-highlighted experience.
Usage: batgrep [OPTIONS] PATTERN [PATH...]
Arguments:
[OPTIONS]
See Options below
PATTERN
Pattern passed to ripgrep
[PATH...]
Path(s) to search
Options:
-i, --ignore-case:
Use case insensitive searching.
-s, --case-sensitive:
Use case sensitive searching.
-S, --smart-case:
Use smart case searching
-A, --after-context=[LINES]:
Display the next n lines after a matched line.
-B, --before-context=[LINES]:
Display the previous n lines before a matched line.
-C, --context=[LINES]:
A combination of --after-context and --before-context
-p, --search-pattern:
Tell pager to search for PATTERN. Currently supported pagers: less.
--no-follow:
Do not follow symlinks
--no-snip:
Do not show the snip decoration
This is automatically enabled when --context=0 or when bat --version is less than 0.12.x
--no-highlight:
Do not highlight matching lines.
This is automatically enabled when --context=0.
--color:
Force color output.
--no-color:
Force disable color output.
--paging=["never"/"always"]:
Enable/disable paging.
--pager=[PAGER]:
Specify the pager to use.
--terminal-width=[COLS]:
Generate output for the specified terminal width.
--no-separator:
Disable printing separator between files.
--rga:
Use ripgrep-all instead of ripgrep.
Options passed directly to ripgrep:
-F, --fixed-strings
-U, --multiline
-P, --pcre2
-z, --search-zip
-w, --word-regexp
--one-file-system
--multiline-dotall
--ignore, --no-ignore
--crlf, --no-crlf
--hidden, --no-hidden
-E --encoding:
This is unsupported by bat, and may cause issues when trying to display unsupported encodings.
-g, --glob
-t, --type
-T, --type-not
-m, --max-count
--max-depth
--iglob
--ignore-file
EOF
}
# -----------------------------------------------------------------------------
# Options:
# -----------------------------------------------------------------------------
RIPGREP="$EXECUTABLE_RIPGREP"
RG_ARGS=()
BAT_ARGS=()
PATTERN=""
@ -157,61 +34,24 @@ OPT_SNIP=""
OPT_HIGHLIGHT=true
OPT_SEARCH_PATTERN=false
OPT_FIXED_STRINGS=false
OPT_NO_SEPARATOR=false
BAT_STYLE="${BAT_STYLE:-header,numbers}"
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 RIPGREP_CONFIG_PATH.
if [[ -n "$RIPGREP_CONFIG_PATH" && -e "$RIPGREP_CONFIG_PATH" ]]; then
# shellcheck disable=SC2046
setargs $(cat "$RIPGREP_CONFIG_PATH")
while shiftopt; do
case "$OPT" in
-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"
;;
esac
done
fi
# Parse arguments.
shopt -s extglob # Needed to handle -u
# First handle -u specially - it can be repeated multiple times in a single
# short argument, and repeating it 1, 2, or 3 times causes different effects.
resetargs
SHIFTOPT_SHORT_OPTIONS="PASS"
while shiftopt; do
case "$OPT" in
[-]+(u) )
RG_ARGS+=("$OPT")
;;
esac
done
resetargs
SHIFTOPT_SHORT_OPTIONS="VALUE"
while shiftopt; do
case "$OPT" in
# ripgrep options
[-]+([u]) ) ;; # Ignore - handled in first loop.
--unrestricted)
RG_ARGS+=("$OPT")
;;
-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"
@ -255,15 +95,6 @@ while shiftopt; do
--no-highlight) OPT_HIGHLIGHT=false ;;
-p | --search-pattern) OPT_SEARCH_PATTERN=true ;;
--no-search-pattern) OPT_SEARCH_PATTERN=false ;;
--no-separator) OPT_NO_SEPARATOR=true ;;
--rga) {
if ! rga --version | grep 'ripgrep-all' &>/dev/null; then
printc "%{RED}%s: option '--rga' requires ripgrep-all to be installed%{CLEAR}\n" "$PROGRAM" 1>&2
exit 1
fi
RIPGREP='rga'
};;
# Option forwarding
--rg:*) {
@ -277,9 +108,6 @@ while shiftopt; do
fi
} ;;
# --
--) getargs -a FILES; break ;;
# ???
-*) {
printc "%{RED}%s: unknown option '%s'%{CLEAR}\n" "$PROGRAM" "$OPT" 1>&2
@ -287,22 +115,25 @@ while shiftopt; do
} ;;
# Search
*) FILES+=("$OPT") ;;
*) {
if [ -z "$PATTERN" ]; then
PATTERN="$OPT"
else
FILES+=("$OPT")
fi
} ;;
esac
done
# Use the first file as a pattern.
PATTERN="${FILES[0]}"
FILES=("${FILES[@]:1}")
if [[ -z "$PATTERN" ]]; then
print_error "no pattern provided"
exit 1
fi
# Generate separator.
SEP="$(printc "%{DIM}%${OPT_TERMINAL_WIDTH}s%{CLEAR}" | sed "s/ /─/g")"
COLS="$(tput cols)"
SEP="$(printc "%{DIM}%${COLS}s%{CLEAR}" | sed "s/ /─/g")"
# Append ripgrep and bat arguments.
if [[ -n "$OPT_CASE_SENSITIVITY" ]]; then
@ -360,50 +191,12 @@ main() {
LAST_LR=()
LAST_LH=()
LAST_FILE=''
READ_FROM_STDIN=false
NO_SEPARATOR="$OPT_NO_SEPARATOR"
if [[ "$BAT_STYLE" = *grid* ]]; then
NO_SEPARATOR=true
fi
# If we found no files being provided and STDIN to not be attached to a tty,
# we capture STDIN to a variable. This variable will later be written to
# the STDIN file descriptors of both ripgrep and bat.
if ! [[ -t 0 ]] && [[ "${#FILES[@]}" -eq 0 ]]; then
READ_FROM_STDIN=true
IFS='' STDIN_DATA="$(cat)"
fi
do_ripgrep_search() {
local COMMON_RG_ARGS=(
--with-filename \
--vimgrep \
"${RG_ARGS[@]}" \
--context 0 \
--sort path \
-- \
"$PATTERN" \
"${FILES[@]}" \
)
if "$READ_FROM_STDIN"; then
"$RIPGREP" "${COMMON_RG_ARGS[@]}" <<< "$STDIN_DATA"
return $?
else
"$RIPGREP" "${COMMON_RG_ARGS[@]}"
return $?
fi
}
do_print() {
[[ -z "$LAST_FILE" ]] && return 0
# Print the separator.
if ! "$NO_SEPARATOR"; then
"$FIRST_PRINT" && echo "$SEP"
fi
"$FIRST_PRINT" && echo "$SEP"
FIRST_PRINT=false
# Print the file.
@ -412,34 +205,19 @@ main() {
"${LAST_LH[@]}" \
--style="${BAT_STYLE}${OPT_SNIP}" \
--paging=never \
--terminal-width="$OPT_TERMINAL_WIDTH" \
--terminal-width="$COLS" \
"$LAST_FILE"
# Print the separator.
if ! "$NO_SEPARATOR"; then
echo "$SEP"
fi
}
do_print_from_file_or_stdin() {
if [[ "$LAST_FILE" = "<stdin>" ]]; then
# If the file is from STDIN, we provide the STDIN
# contents to bat and tell it to read from STDIN.
LAST_FILE="-"
do_print <<< "$STDIN_DATA"
return $?
else
do_print
return $?
fi
echo "$SEP"
}
# shellcheck disable=SC2034
while IFS=':' read -r file line column text; do
while IFS=':' read -r file line column; do
((FOUND++))
if [[ "$LAST_FILE" != "$file" ]]; then
do_print_from_file_or_stdin
do_print
LAST_FILE="$file"
LAST_LR=()
LAST_LH=()
@ -452,8 +230,8 @@ main() {
LAST_LR+=("--line-range=${line_start}:${line_end}")
[[ "$OPT_HIGHLIGHT" = "true" ]] && LAST_LH+=("--highlight-line=${line}")
done < <(do_ripgrep_search)
do_print_from_file_or_stdin
done < <(rg --with-filename --vimgrep "${RG_ARGS[@]}" --sort path "$PATTERN" "${FILES[@]}")
do_print
# Exit.
if [[ "$FOUND" -eq 0 ]]; then

View file

@ -6,110 +6,36 @@
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
# shellcheck disable=SC1090 disable=SC2155
SELF_NC="${BASH_SOURCE:-$0}"
SELF="$(cd "$(dirname "${SELF_NC}")" && cd "$(dirname "$(readlink "${SELF_NC}" || echo ".")")" && pwd)/$(basename "$(readlink "${SELF_NC}" || echo "${SELF_NC}")")"
LIB="$(cd "$(dirname "${SELF_NC}")" && cd "$(dirname "$(readlink "${SELF_NC}" || echo ".")")/../lib" && pwd)"
LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo ".")")/../lib" && pwd)"
if [[ -n "${MANPAGER}" ]]; then BAT_PAGER="$MANPAGER"; fi
source "${LIB}/constants.sh"
source "${LIB}/pager.sh"
source "${LIB}/print.sh"
source "${LIB}/opt.sh"
source "${LIB}/opt_hook_color.sh"
source "${LIB}/opt_hook_version.sh"
source "${LIB}/opt_hooks.sh"
# -----------------------------------------------------------------------------
hook_color
hook_pager
hook_version
# -----------------------------------------------------------------------------
FORWARDED_ARGS=()
MAN_ARGS=()
BAT_ARGS=()
OPT_EXPORT_ENV=false
SHIFTOPT_SHORT_OPTIONS="SPLIT"
while shiftopt; do
case "$OPT" in
--export-env) OPT_EXPORT_ENV=true ;;
--paging|--pager|--wrap) shiftval; FORWARDED_ARGS+=("${OPT}=${OPT_VAL}");
BAT_ARGS+=("${OPT}=${OPT_VAL}") ;;
*) MAN_ARGS+=("$OPT") ;;
esac
done
while shiftopt; do MAN_ARGS+=("$OPT"); done
if "$OPT_COLOR"; then
BAT_ARGS+=("--color=always" "--decorations=always")
BAT_ARGS=("--color=always --decorations=always")
else
BAT_ARGS+=("--color=never" "--decorations=never")
BAT_ARGS=("--color=never --decorations=never")
fi
if [[ -z "${BAT_STYLE+x}" ]]; then
export BAT_STYLE="grid"
fi
# -----------------------------------------------------------------------------
# When called as the manpager, do some preprocessing and feed everything to bat.
if [[ "${BATMAN_IS_BEING_MANPAGER:-}" = "yes" ]]; then
print_manpage() {
sed -e 's/\x1B\[[0-9;]*m//g; s/.\x08//g' \
| "$EXECUTABLE_BAT" --language=man "${BAT_ARGS[@]}"
exit $?
}
if [[ "${#MAN_ARGS[@]}" -eq 1 ]]; then
# The input was passed as a file.
cat "${MAN_ARGS[0]}" | print_manpage
else
# The input was passed via stdin.
cat | print_manpage
fi
exit
fi
# -----------------------------------------------------------------------------
if [[ -n "${MANPAGER}" ]]; then BAT_PAGER="$MANPAGER"; fi
export MANPAGER="env BATMAN_IS_BEING_MANPAGER=yes bash $(printf "%q " "$SELF" "${FORWARDED_ARGS[@]}")"
export MANPAGER="${MANPAGER%"${MANPAGER##*[![:space:]]}"}"
export MANPAGER='sh -c "col -bx | '"$(printf "%q" "$EXECUTABLE_BAT")"' --language=man --style=grid '"${BAT_ARGS[*]}"'"'
export MANROFFOPT='-c'
# If `--export-env`, print exports to use batman as the manpager directly.
if "$OPT_EXPORT_ENV"; then
printf "export %s=%q\n" \
"MANPAGER" "$MANPAGER" \
"MANROFFOPT" "$MANROFFOPT"
exit 0
if [[ -n "${SCRIPT_PAGER_CMD}" ]]; then
export BAT_PAGER="$(printf "%q " "${SCRIPT_PAGER_CMD[@]}" "${SCRIPT_PAGER_ARGS[@]}")"
else
unset BAT_PAGER
fi
# If no argument is provided and fzf is installed, use fzf to search for man pages.
if [[ "${#MAN_ARGS[@]}" -eq 0 ]] && [[ -z "$BATMAN_LEVEL" ]] && command -v "$EXECUTABLE_FZF" &>/dev/null; then
export BATMAN_LEVEL=1
selected_page="$(man -k . | "$EXECUTABLE_FZF" --delimiter=" - " --reverse -e --preview="
echo {1} \
| sed 's/, /\n/g;' \
| sed 's/\([^(]*\)(\([0-9A-Za-z ]\))/\2\t\1/g' \
| BAT_STYLE=plain xargs -n2 batman --color=always --paging=never
")"
if [[ -z "$selected_page" ]]; then
exit 0
fi
# Some entries from `man -k .` may include "synonyms" of a man page's title.
# For example, the manual for kubectl-edit will appear as:
#
# kubectl-edit(1), kubectl edit(1) - Edit a resource on the server
#
# `man` only needs one name/title, so we're taking the first one here.
selected_page_unaliased="$(echo "$selected_page" | cut -d, -f1)"
# Convert the page(section) format to something that can be fed to the man command.
while read -r line; do
if [[ "$line" =~ ^(.*)\(([0-9a-zA-Z ]+)\) ]]; then
MAN_ARGS+=("${BASH_REMATCH[2]}" "$(echo ${BASH_REMATCH[1]} | xargs)")
fi
done <<< "$selected_page_unaliased"
fi
# Run man.
command man "${MAN_ARGS[@]}"
exit $?

View file

@ -1,523 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2021 eth-p | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
#
# EXTERNAL VIEWERS FOR BATPIPE:
#
# External viewers can be added to batpipe by creating bash scripts
# inside the `~/.config/batpipe/viewers.d/` directory.
#
# CREATING A VIEWER:
#
# Viewers must define two functions and append the viewer's name to the
# `BATPIPE_VIEWERS` array.
#
# - viewer_${viewer}_supports [file_basename] [file_path] [inner_file_path]
# If this returns 0, the viewer's process function will be used.
#
# - viewer_${viewer}_process [file_path] [inner_file_path]
#
# VIEWER API:
#
# $BATPIPE_VIEWERS -- An array of loaded file viewers.
# $BATPIPE_ENABLE_COLOR -- Whether color is supported. (`true`|`false`)
# $BATPIPE_INSIDE_LESS -- Whether batpipe is inside less. (`true`|`false`)
# $TERM_WIDTH -- The terminal width. (only supported in `less`)
#
# batpipe_header [pattern] [...] -- Print a viewer header line.
# batpipe_subheader [pattern] [...] -- Print a viewer subheader line.
#
# bat -- Use `bat` for highlighting. If running inside `bat`, does nothing.
#
# strip_trailing_slashes [path] -- Strips trailing slashes from a path.
#
# -----------------------------------------------------------------------------
# shellcheck disable=SC1090 disable=SC2155
SELF_NC="${BASH_SOURCE:-$0}"
SELF="$(cd "$(dirname "${SELF_NC}")" && cd "$(dirname "$(readlink "${SELF_NC}" || echo ".")")" && pwd)/$(basename "${SELF_NC}")"
LIB="$(cd "$(dirname "${SELF_NC}")" && cd "$(dirname "$(readlink "${SELF_NC}" || echo ".")")/../lib" && pwd)"
source "${LIB}/constants.sh"
source "${LIB}/dirs.sh"
source "${LIB}/str.sh"
source "${LIB}/print.sh"
source "${LIB}/path.sh"
source "${LIB}/proc.sh"
source "${LIB}/opt.sh"
source "${LIB}/version.sh"
source "${LIB}/term.sh"
# -----------------------------------------------------------------------------
# Usage/Install:
# -----------------------------------------------------------------------------
if [[ "$#" -eq 0 ]]; then
# If writing to a terminal, display instructions and help.
if [[ -t 1 ]]; then
printc "%{DIM}# %s, %s.\n# %s\n# %s\n# %s\n# \n# %s%{CLEAR}\n" \
"$PROGRAM" \
"a bat-based preprocessor for less and bat" \
"Version: $PROGRAM_VERSION" \
"Homepage: $PROGRAM_HOMEPAGE" \
"$PROGRAM_COPYRIGHT" \
"To use $PROGRAM, eval the output of this command in your shell init script."
fi
# Detect the shell.
#
# This will directly check if the parent is a non-sh/bash shell, since
# there's a good chance that `bash` or `sh` will be invoking it.
case "$(basename -- "$(parent_executable | cut -f1 -d' ')")" in
fish) detected_shell="fish" ;;
nu) detected_shell="nu" ;;
*) detected_shell="$(parent_shell)" ;;
esac
# Print the commands required to add `batpipe` to the environment variables.
case "$(basename -- "${detected_shell:bash}")" in
fish) # Fish
printc '%{YELLOW}set -x %{CLEAR}LESSOPEN %{CYAN}"|%q %%s"%{CLEAR};\n' "$SELF"
printc '%{YELLOW}set -e %{CLEAR}LESSCLOSE;\n'
;;
nu) # Nushell
printc '%{BLUE}$env%{CLEAR}.LESSOPEN = %{CYAN}"|%q %%s"%{CLEAR}\n' "$SELF"
if [[ "${LESSCLOSE:-}" != "" ]]; then
printc '%{BLUE}hide-env%{CLEAR} LESSCLOSE\n' "$SELF"
fi
;;
*) # Bash-like
printc '%{MAGENTA}LESSOPEN%{CLEAR}=%{CYAN}"|%s %%s"%{CLEAR};\n' "$SELF"
printc '%{YELLOW}export%{CLEAR} LESSOPEN;\n' "$SELF"
printc '%{YELLOW}unset%{CLEAR} LESSCLOSE;\n'
;;
esac
# Print the commands required to use color in `less` with `batpipe`.
if [[ -t 1 ]]; then
printc "\n%{DIM}# The following will enable colors when using batpipe with less:%{CLEAR}\n"
fi
# shellcheck disable=SC2016
case "$(basename -- "${detected_shell:bash}")" in
fish) # Fish
printc '%{YELLOW}set -x %{CLEAR}LESS %{CYAN}"%{MAGENTA}$LESS%{CYAN} -R"%{CLEAR};\n' "$SELF"
printc '%{YELLOW}set -x %{CLEAR}BATPIPE %{CYAN}"color"%{CLEAR};\n'
;;
nu) # Nushell
printc '%{BLUE}$env%{CLEAR}.LESS = %{CYAN}$"%{MAGENTA}($env.LESS)%{CYAN} -R"%{CLEAR}\n' "$SELF"
printc '%{BLUE}$env%{CLEAR}.BATPIPE = %{CYAN}"color"%{CLEAR}\n' "$SELF"
;;
*) # Bash-like
printc '%{MAGENTA}LESS%{CLEAR}=%{CYAN}"%{MAGENTA}$LESS%{CYAN} -R"%{CLEAR};\n' "$SELF"
printc '%{MAGENTA}BATPIPE%{CLEAR}=%{CYAN}"color"%{CLEAR};\n' "$SELF"
printc '%{YELLOW}export%{CLEAR} LESS;\n' "$SELF"
printc '%{YELLOW}export%{CLEAR} BATPIPE;\n' "$SELF"
;;
esac
exit 0
fi
# -----------------------------------------------------------------------------
# Init:
# -----------------------------------------------------------------------------
BATPIPE_INSIDE_LESS=false
BATPIPE_INSIDE_BAT=false
TERM_WIDTH="$(term_width)"
if [[ -n "${BATPIPE_TERM_WIDTH:-}" ]]; then
if [[ "${BATPIPE_TERM_WIDTH:0:1}" = "-" ]]; then
TERM_WIDTH=$((TERM_WIDTH + BATPIPE_TERM_WIDTH)) || true
else
TERM_WIDTH="$BATPIPE_TERM_WIDTH"
fi
fi
BATPIPE_PARENT_EXECUTABLE_PID="$PPID"
for i in 1 2 3; do
BATPIPE_PARENT_EXECUTABLE="${BATPIPE_DEBUG_PARENT_EXECUTABLE:-$(parent_executable "$BATPIPE_PARENT_EXECUTABLE_PID")}"
BATPIPE_PARENT_EXECUTABLE_BASENAME="$(basename -- "${BATPIPE_PARENT_EXECUTABLE}" | cut -d' ' -f1)"
BATPIPE_PARENT_EXECUTABLE_PID="$(parent_executable_pid "$BATPIPE_PARENT_EXECUTABLE_PID")"
if [[ "${BATPIPE_PARENT_EXECUTABLE_BASENAME}" = "less" ]]; then
BATPIPE_INSIDE_LESS=true
break
elif [[ "${BATPIPE_PARENT_EXECUTABLE_BASENAME}" == "$(basename -- "$EXECUTABLE_BAT")" ]]; then
BATPIPE_INSIDE_BAT=true
break
fi
done
if [[ -n "${BATPIPE_DEBUG:-}" ]]; then
printf "batpipe debug:\n"
printf " %s: %s\n" \
"BATPIPE_INSIDE_LESS" "${BATPIPE_INSIDE_LESS}" \
"BATPIPE_INSIDE_BAT" "${BATPIPE_INSIDE_BAT}"
printf "\n"
fi
# -----------------------------------------------------------------------------
# Viewers:
# -----------------------------------------------------------------------------
if ! command -v eza &> /dev/null
then
BATPIPE_VIEWERS=("eza" "ls" "tar" "tar_gz" "unzip" "gunzip" "xz")
else
BATPIPE_VIEWERS=("exa" "ls" "tar" "tar_bz2" "unzip" "gunzip" "xz")
fi
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
viewer_exa_supports() {
[[ -d "$2" ]] || return 1
command -v "exa" &> /dev/null || return 1
return 0
}
viewer_eza_supports() {
[[ -d "$2" ]] || return 1
command -v "eza" &> /dev/null || return 1
return 0
}
viewer_exa_process() {
local dir="$(strip_trailing_slashes "$1")"
batpipe_header "Viewing contents of directory: %{PATH}%s" "$dir"
if "$BATPIPE_ENABLE_COLOR"; then
exa -la --color=always "$1" 2>&1
else
exa -la --color=never "$1" 2>&1
fi
return $?
}
viewer_eza_process() {
local dir="$(strip_trailing_slashes "$1")"
batpipe_header "Viewing contents of directory: %{PATH}%s" "$dir"
if "$BATPIPE_ENABLE_COLOR"; then
eza -la --color=always "$1" 2>&1
else
eza -la --color=never "$1" 2>&1
fi
return $?
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
viewer_ls_supports() {
[[ -d "$2" ]]
return $?
}
viewer_ls_process() {
local dir="$(strip_trailing_slashes "$1")"
batpipe_header "Viewing contents of directory: %{PATH}%s" "$dir"
ls -lA "$1" 2>&1
return $?
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
viewer_tar_supports() {
command -v "tar" &> /dev/null || return 1
case "$1" in
*.tar | *.tar.*) return 0 ;;
esac
return 1
}
viewer_tar_process() {
if [[ -n "$2" ]]; then
tar $3 -xf "$1" -O "$2" | bat --file-name="$1/$2"
else
batpipe_archive_header
tar $3 -tvf "$1"
return $?
fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
viewer_tar_gz_supports() {
command -v "tar" &> /dev/null || return 1
command -v "gzip" &> /dev/null || return 1
case "$1" in
*.tar.gz | *.tgz) return 0 ;;
esac
return 1
}
viewer_tar_gz_process() {
viewer_tar_process "$1" "$2" -z
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
viewer_tar_bz2_supports() {
command -v "tar" &> /dev/null || return 1
command -v "bzip2" &> /dev/null || return 1
case "$1" in
*.tar.bz2 | *.tbz) return 0 ;;
esac
return 1
}
viewer_tar_bz2_process() {
viewer_tar_process "$1" "$2" -j
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
viewer_unzip_supports() {
command -v "unzip" &> /dev/null || return 1
case "$1" in
*.zip) return 0 ;;
esac
return 1
}
viewer_unzip_process() {
if [[ -n "$2" ]]; then
unzip -p "$1" "$2" | bat --file-name="$1/$2"
else
batpipe_archive_header
unzip -l "$1"
return $?
fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
viewer_gunzip_supports() {
command -v "gunzip" &> /dev/null || return 1
[[ -z "$3" ]] || return 1
case "$2" in
*.gz) return 0 ;;
esac
return 1
}
viewer_gunzip_process() {
gunzip -k -c "$1" | bat --file-name="$1"
return $?
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
viewer_xz_supports() {
command -v "xz" &> /dev/null || return 1
[[ -z "$3" ]] || return 1
case "$2" in
*.xz) return 0 ;;
esac
return 1
}
viewer_xz_process() {
xz --decompress -k -c "$1" | bat --file-name="$1"
return $?
}
# -----------------------------------------------------------------------------
# Functions:
# -----------------------------------------------------------------------------
# Print a header for batpipe messages.
# Arguments:
# 1 -- The printc formatting string.
# ... -- The printc formatting arguments.
batpipe_header() {
local pattern="${1//%{C\}/%{C\}%{HEADER\}}"
printc "%{HEADER}==> $pattern%{C}\n" "${@:2}"
}
# Print a subheader for batpipe messages.
# Arguments:
# 1 -- The printc formatting string.
# ... -- The printc formatting arguments.
batpipe_subheader() {
local pattern="${1//%{C\}/%{C\}%{SUBHEADER\}}"
printc "%{SUBHEADER}==> $pattern%{C}\n" "${@:2}"
}
batpipe_archive_header() {
batpipe_header "Viewing contents of archive: %{PATH}%s" "$1"
batpipe_subheader "To view files within the archive, add the file path after the archive."
}
# Executes `bat` (or `cat`, if already running from within `bat`).
# Supports the `--file-name` argument if the bat version is new enough.
#
# NOTE: The `--key=value` option syntax is required for full compatibility.
bat() {
# Conditionally enable forwarding of certain arguments.
if [[ -z "$__BAT_VERSION" ]]; then
__BAT_VERSION="$(bat_version)"
__bat_forward_arg_file_name() { :; }
if version_compare "$__BAT_VERSION" -ge "0.14"; then
__bat_forward_arg_file_name() {
__bat_forward_args+=("--file-name" "$1")
}
fi
fi
# Parse arguments intended for bat.
__bat_batpipe_args=()
__bat_forward_args=()
__bat_files=()
setargs "$@"
while shiftopt; do
case "$OPT" in
--file-name)
shiftval
__bat_forward_arg_file_name "$OPT_VAL"
;;
# Disallowed forwarding.
--paging) shiftval ;;
--decorations) shiftval ;;
--style) shiftval ;;
--terminal-width) shiftval ;;
--plain | -p | -pp | -ppp) : ;;
# Forward remaining.
-*) {
__bat_forward_args+=("$OPT")
if [[ -n "$OPT_VAL" ]]; then
__bat_forward_args+=("$OPT_VAL")
fi
} ;;
*) {
__bat_forward_args+=("$OPT")
__bat_files=("$OPT")
} ;;
esac
done
# Insert batpipe arguments.
if "$BATPIPE_INSIDE_LESS"; then
__bat_batpipe_args+=(--decorations=always)
__bat_batpipe_args+=(--terminal-width="$TERM_WIDTH")
if "$BATPIPE_ENABLE_COLOR"; then
__bat_batpipe_args+=(--color=always)
else
__bat_batpipe_args+=(--color=never)
fi
fi
if "$BATPIPE_INSIDE_BAT"; then
if [[ "${#__bat_files[@]}" -eq 0 ]]; then
cat
else
cat "${__bat_files[@]}"
fi
fi
# Execute the real bat.
command "$EXECUTABLE_BAT" --paging=never "${__bat_batpipe_args[@]}" "${__bat_forward_args[@]}"
}
# -----------------------------------------------------------------------------
# Colors:
# -----------------------------------------------------------------------------
printc_init "[DEFINE]" << END
C \x1B[0m
SUBPATH \x1B[2;35m
PATH \x1B[0;35m
HEADER \x1B[0;36m
SUBHEADER \x1B[2;36m
END
# Enable color output if:
# - Parent is not less OR BATPIPE=color; AND
# - NO_COLOR is not defined.
#
# shellcheck disable=SC2034
if [[ "$BATPIPE_INSIDE_LESS" == "false" || "$BATPIPE" == "color" ]] && [[ -z "${NO_COLOR+x}" ]]; then
BATPIPE_ENABLE_COLOR=true
printc_init true
else
BATPIPE_ENABLE_COLOR=false
printc_init false
fi
# -----------------------------------------------------------------------------
# Main:
# -----------------------------------------------------------------------------
__CONFIG_DIR="$(config_dir batpipe)"
__TARGET_INSIDE=""
__TARGET_FILE="$(strip_trailing_slashes "$1")"
# Determine the target file by walking upwards from the specified path.
# This allows inner paths of archives to be used.
while ! [[ -e "$__TARGET_FILE" ]]; do
__TARGET_INSIDE="$(basename -- "${__TARGET_FILE}")/${__TARGET_INSIDE}"
__TARGET_FILE="$(dirname -- "${__TARGET_FILE}")"
done
# If the target file isn't actually a file, then the inner path should be appended.
if ! [[ -f "$__TARGET_FILE" ]]; then
__TARGET_FILE="${__TARGET_FILE}/${__TARGET_INSIDE}"
__TARGET_INSIDE=""
fi
# If an inner path exists or the target file isn't a directory, the target file should not have trailing slashes.
if [[ -n "$__TARGET_INSIDE" ]] || ! [[ -d "$__TARGET_FILE" ]]; then
__TARGET_FILE="$(strip_trailing_slashes "$__TARGET_FILE")"
fi
# Remove trailing slash of the inner target path.
__TARGET_INSIDE="$(strip_trailing_slashes "$__TARGET_INSIDE")"
__TARGET_BASENAME="$(basename -- "$__TARGET_FILE")"
# Stop bat from calling this recursively.
unset LESSOPEN
unset LESSCLOSE
# Load external viewers.
if [[ -d "${__CONFIG_DIR}/viewers.d" ]]; then
unset LIB
unset SELF
shopt -s nullglob
for viewer_script in "${__CONFIG_DIR}/viewers.d"/*; do
source "${viewer_script}"
done
shopt -u nullglob
fi
# Try opening the file with the first viewer that supports it.
for viewer in "${BATPIPE_VIEWERS[@]}"; do
if "viewer_${viewer}_supports" "$__TARGET_BASENAME" "$__TARGET_FILE" "$__TARGET_INSIDE" 1>&2; then
"viewer_${viewer}_process" "$__TARGET_FILE" "$__TARGET_INSIDE"
exit $?
fi
done
# No supported viewer. Just pass it through (if using bat).
if [[ "$BATPIPE_INSIDE_BAT" == true ]]; then
exit 1
fi
# No supported viewer... highlight it using bat.
if [[ -f "$1" ]]; then
bat "$1"
fi

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2019-2020 eth-p | MIT License
# 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
@ -9,27 +9,15 @@
LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo ".")")/../lib" && pwd)"
source "${LIB}/constants.sh"
source "${LIB}/opt.sh"
source "${LIB}/opt_hook_color.sh"
source "${LIB}/opt_hook_help.sh"
source "${LIB}/opt_hook_version.sh"
source "${LIB}/opt_hook_width.sh"
source "${LIB}/opt_hooks.sh"
source "${LIB}/print.sh"
source "${LIB}/pager.sh"
source "${LIB}/version.sh"
# -----------------------------------------------------------------------------
# Init:
# -----------------------------------------------------------------------------
hook_color
hook_pager
hook_version
hook_width
hook_help
# -----------------------------------------------------------------------------
# Help:
# -----------------------------------------------------------------------------
show_help() {
echo 'Usage: batwatch --file [--watcher entr|poll][--[no-]clear] <file> [<file> ...]'
echo ' batwatch --command [-n<interval>] <command> [<arg> ...]'
}
# -----------------------------------------------------------------------------
# Watchers:
# -----------------------------------------------------------------------------
@ -41,20 +29,20 @@ WATCHERS=("entr" "poll")
watcher_entr_watch() {
ENTR_ARGS=()
if [[ "$OPT_CLEAR" == "true" ]]; then
if [[ "$OPT_CLEAR" = "true" ]]; then
ENTR_ARGS+=('-c')
fi
entr "${ENTR_ARGS[@]}" \
"$EXECUTABLE_BAT" "${BAT_ARGS[@]}" \
--terminal-width="$OPT_TERMINAL_WIDTH" \
--terminal-width="$TERM_WIDTH" \
--paging=never \
"$@" \
< <(printf "%s\n" "$@")
}
watcher_entr_supported() {
command -v entr &> /dev/null
command -v entr &>/dev/null
return $?
}
@ -68,22 +56,19 @@ determine_stat_variant() {
return 0
fi
local variant name flags ts
for variant in "gnu -c %Z" "bsd -f %m"; do
read -r name flags <<< "$variant"
# Try GNU stat.
if stat -c '%z' "$0" &>/dev/null; then
POLL_STAT_COMMAND=(stat -c '%z')
POLL_STAT_VARIANT='gnu'
return 0
fi
# save the results of the stat command
if read -r ts < <(stat ${flags} "$0" 2> /dev/null); then
# verify that the value is an epoch timestamp
# before proceeding
if [[ "${ts}" =~ ^[0-9]+$ ]]; then
POLL_STAT_COMMAND=(stat ${flags})
POLL_STAT_VARIANT="$name"
return 0
fi
fi
done
# Try BSD stat.
if stat -f '%m' "$0" &>/dev/null; then
POLL_STAT_COMMAND=(stat -f '%m')
POLL_STAT_VARIANT='bsd'
return 0
fi
return 1
}
@ -107,15 +92,17 @@ watcher_poll_watch() {
while true; do
if "$modified"; then
modified=false
clear
if [[ "$OPT_CLEAR" = "true" ]]; then
clear
fi
"$EXECUTABLE_BAT" "${BAT_ARGS[@]}" \
--terminal-width="$OPT_TERMINAL_WIDTH" \
--terminal-width="$TERM_WIDTH" \
--paging=never \
"${files[@]}"
fi
# Check if the file has been modified.
local i=0
for file in "${files[@]}"; do
time="$("${POLL_STAT_COMMAND[@]}" "$file")"
@ -128,12 +115,7 @@ watcher_poll_watch() {
((i++))
done
# Wait for "q" to exit, or check again after a few seconds.
local input
read -r -t "${OPT_INTERVAL}" input
if [[ "$input" =~ [q|Q] ]]; then
exit
fi
sleep 1
done
"${POLL_STAT_COMMAND[@]}" "$@"
@ -153,7 +135,7 @@ determine_watcher() {
local watcher
for watcher in "${WATCHERS[@]}"; do
if "watcher_${watcher}_supported"; then
OPT_WATCHER="$watcher"
echo "$watcher"
return 0
fi
done
@ -164,13 +146,12 @@ determine_watcher() {
# -----------------------------------------------------------------------------
# Options:
# -----------------------------------------------------------------------------
BAT_ARGS=(--paging=never)
BAT_ARGS=()
FILES=()
FILES_HAS_DIRECTORY=false
OPT_MODE=file
OPT_INTERVAL=3
OPT_CLEAR=true
OPT_WATCHER=""
TERM_WIDTH="$(tput cols)"
# Set options based on tty.
if [[ -t 1 ]]; then
@ -181,52 +162,38 @@ fi
while shiftopt; do
case "$OPT" in
# Script options
--watcher) shiftval; OPT_WATCHER="$OPT_VAL" ;;
--interval|-n) shiftval; OPT_INTERVAL="$OPT_VAL" ;;
--file|-f) OPT_MODE=file ;;
--command|-x) OPT_MODE=command ;;
--clear) OPT_CLEAR=true ;;
--no-clear) OPT_CLEAR=false ;;
# Script options
--watcher) shiftval; OPT_WATCHER="$OPT_VAL" ;;
--clear) OPT_CLEAR=true ;;
--no-clear) OPT_CLEAR=false ;;
--terminal-width) shiftval; TERM_WIDTH="$OPT_VAL" ;;
# bat/Pager options
-*) BAT_ARGS+=("$OPT=$OPT_VAL") ;;
# bat/Pager options
-*) BAT_ARGS+=("$OPT=$OPT_VAL") ;;
# Files
*) {
FILES+=("$OPT")
if [[ "$OPT_MODE" = "command" ]]; then
getargs --append FILES
break
fi
} ;;
# Files
*) {
FILES+=("$OPT")
} ;;
esac
done
# Validate that a file/command was provided.
if [[ ${#FILES[@]} -eq 0 ]]; then
if [[ "$OPT_MODE" = "file" ]]; then
print_error "no files provided"
else
print_error "no command provided"
fi
print_error "no files provided"
exit 1
fi
# Validate that the provided files exist.
if [[ "$OPT_MODE" = "file" ]]; then
for file in "${FILES[@]}"; do
if ! [[ -e "$file" ]]; then
print_error "'%s' does not exist" "$file"
exit 1
fi
if [[ -d "$file" ]]; then
FILES_HAS_DIRECTORY=true
fi
done
fi
for file in "${FILES[@]}"; do
if ! [[ -e "$file" ]]; then
print_error "'%s' does not exist" "$file"
exit 1
fi
if [[ -d "$file" ]]; then
FILES_HAS_DIRECTORY=true
fi
done
# Append bat arguments.
if "$OPT_COLOR"; then
@ -235,76 +202,33 @@ else
BAT_ARGS+=("--color=never")
fi
# Initialize clear command based on whether or not ANSI should be used.
if [[ "$OPT_CLEAR" == "true" ]]; then
if "$OPT_COLOR"; then
clear() {
term_clear || return $?
}
fi
else
clear() {
:
}
fi
# -----------------------------------------------------------------------------
# Main:
# -----------------------------------------------------------------------------
if [[ "$OPT_MODE" = "file" ]]; then
# Determine the watcher.
if [[ -z "$OPT_WATCHER" ]]; then
if ! determine_watcher; then
print_error "Your system does not have any supported watchers."
printc "Please read the documentation at %{BLUE}%s%{CLEAR} for more details.\n" "$PROGRAM_HOMEPAGE" 1>&2
exit 2
fi
else
if ! type "watcher_${OPT_WATCHER}_supported" &> /dev/null; then
print_error "Unknown watcher: '%s'" "$OPT_WATCHER"
exit 1
fi
if ! "watcher_${OPT_WATCHER}_supported" &> /dev/null; then
print_error "Unsupported watcher: '%s'" "$OPT_WATCHER"
exit 1
fi
# Determine the watcher.
if [[ -z "$OPT_WATCHER" ]]; then
if ! OPT_WATCHER="$(determine_watcher)"; then
print_error "Your system does not have any supported watchers."
printc "Please read the documentation at %{BLUE}%s%{CLEAR} for more details.\n" "$PROGRAM_HOMEPAGE" 1>&2
exit 2
fi
main() {
"watcher_${OPT_WATCHER}_watch" "${FILES[@]}"
return $?
}
else
# Set bat's header to show the command.
BAT_VERSION="$(bat_version)"
if version_compare "$BAT_VERSION" -ge "0.14"; then
BAT_ARGS+=(--file-name="${FILES[*]}")
if ! type "watcher_${OPT_WATCHER}_supported" &>/dev/null; then
print_error "Unknown watcher: '%s'" "$OPT_WATCHER"
exit 1
fi
main() {
local last_rendered
local rendered
local term_width="$(term_width)"
BAT_ARGS+=("--terminal-width=$term_width")
while true; do
IFS='' rendered="$("${FILES[@]}" 2>&1 | "$EXECUTABLE_BAT" "${BAT_ARGS[@]}")"
if [ "$rendered" != "$last_rendered" ]; then
# Only clear and redraw if there's a change.
# This reduces excessive flickering.
last_rendered="$rendered"
clear
printf "%s\n" "$rendered"
rendered=''
fi
sleep "${OPT_INTERVAL}" || exit 1
done
}
if ! "watcher_${OPT_WATCHER}_supported" &>/dev/null; then
print_error "Unsupported watcher: '%s'" "$OPT_WATCHER"
exit 1
fi
fi
# Run the main function.
main
main() {
"watcher_${OPT_WATCHER}_watch" "${FILES[@]}"
return $?
}
pager_exec main
exit $?

View file

@ -12,9 +12,6 @@ source "${LIB}/opt.sh"
source "${LIB}/opt_hook_version.sh"
source "${LIB}/str.sh"
source "${LIB}/print.sh"
source "${LIB}/version.sh"
source "${LIB}/check.sh"
source "${LIB}/term.sh"
# -----------------------------------------------------------------------------
# Init:
# -----------------------------------------------------------------------------
@ -23,20 +20,16 @@ hook_version
# Formatters:
# -----------------------------------------------------------------------------
FORMATTERS=(
"yq" "prettier" "rustfmt" "shfmt" "clangformat"
"black" "mix_format" "column"
)
FORMATTERS=("prettier" "rustfmt" "shfmt" "clangformat")
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
formatter_prettier_supports() {
case "$1" in
.js | .jsx | \
.js | .jsx | \
.ts | .tsx | \
.css | .scss | .sass | \
.graphql | .gql | \
.html | .svg | \
.html | \
.json | \
.md | \
.yml)
@ -48,14 +41,7 @@ formatter_prettier_supports() {
}
formatter_prettier_process() {
# Rewrite the file extension to hackily support SVG.
local file="$1"
local fext="$(extname "$file")"
case "$fext" in
.svg) file="$(basename -- "$file" "$fext").html" ;;
esac
prettier --stdin --stdin-filepath "$file" 2>/dev/null
prettier --stdin --stdin-filepath "$1" 2>/dev/null
return $?
}
@ -102,122 +88,12 @@ formatter_shfmt_process() {
return $?
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
formatter_black_supports() {
case "$1" in
.py | \
.py3 | \
.pyw | \
.pyi)
return 0
;;
esac
return 1
}
formatter_black_process() {
black --code "$(cat -)"
return $?
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
formatter_mix_format_supports() {
case "$1" in
.ex | \
.exs | \
.eex | \
.heex)
return 0
;;
esac
return 1
}
formatter_mix_format_process() {
mix format
return $?
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
formatter_column_supports() {
case "$1" in
.tsv)
return 0
;;
esac
return 1
}
formatter_column_process() {
local needs_newline=true
local args=(
-t
-s $'\t'
-c "$TERMINAL_WIDTH"
)
if column --help &>/dev/null; then
# GNU `column`
args+=(--keep-empty-lines)
needs_newline=false
fi
{ { "$needs_newline" && sed 's/$/\n/'; } || cat; } | column "${args[@]}"
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
formatter_yq_supports__version_ok() {
local yq_version
yq_version=$(yq --version | sed 's/^.* version v//')
# If it's older than yq version 4, replace the functions to save processing.
if version_compare "$yq_version" -lt 4.0; then
formatter_yq_supports() { return 1; }
formatter_yq_supports__version_ok() { return 1; }
return 1
fi
# It's supported.
formatter_yq_supports__version_ok() { return 0; }
return 0
}
formatter_yq_supports() {
case "$1" in
.yaml | yml | \
.json)
formatter_yq_supports__version_ok
return $?
;;
esac
return 1
}
formatter_yq_process() {
local args=()
case "$1" in
*.json) args+=(--output-format json) ;;
esac
yq --prettyPrint --indent 4 "${args[@]}" \
| sed -e ':l' -e 's/^\(\t*\) /\1\t/g; t l'
return $?
}
# -----------------------------------------------------------------------------
# Functions:
# -----------------------------------------------------------------------------
# This function will map a bat `--language=...` argument into an appropriate
# file extension for the language provided. This should be hardcoded for
# file extension for the language provided. This must be hardcoded for
# performance reasons.
map_language_to_extension() {
local ext=".txt"
@ -231,17 +107,11 @@ map_language_to_extension() {
css) ext=".css" ;;
scss) ext=".scss" ;;
sass) ext=".sass" ;;
svg ) ext=".svg" ;;
html | htm | shtml | xhtml) ext=".html" ;;
json) ext=".json" ;;
md | mdown | markdown) ext=".md" ;;
yaml | yml) ext=".yml" ;;
rust | rs) ext=".rs" ;;
graphql | gql) ext=".graphql" ;;
python | py) ext=".py" ;;
elixir | ex) ext=".ex" ;;
exs) ext=".exs" ;;
tsv) ext=".tsv" ;;
esac
echo "$ext"
@ -268,38 +138,28 @@ extname() {
}
print_file() {
if [[ "${#PRINT_ARGS[@]}" -eq 0 ]]; then
if [[ "${#BAT_ARGS[@]}" -eq 0 ]]; then
"$EXECUTABLE_BAT" "$@"
return $?
else
"$EXECUTABLE_BAT" "${PRINT_ARGS[@]}" "$@"
"$EXECUTABLE_BAT" "${BAT_ARGS[@]}" "$@"
return $?
fi
}
process_file() {
PRINT_ARGS=("${BAT_ARGS[@]}")
local file="$1"
local ext="$2"
local fext="$ext"
local lang="${ext:1}"
local formatter
# Check that the file exists, and is a file.
if [[ "$file" != "-" ]]; then
check_exists "$file" || return 1
check_is_file "$file" || return 1
fi
# Determine the formatter.
if [[ -n "$OPT_LANGUAGE" ]]; then
lang="$OPT_LANGUAGE"
fext="$(map_language_to_extension "$lang")"
fi
if [[ "$ext" != "-" ]]; then
formatter="$(map_extension_to_formatter "$fext")"
fi
formatter="$(map_extension_to_formatter "$fext")"
# Debug: Print the name and formatter.
if "$DEBUG_PRINT_FORMATTER"; then
@ -307,9 +167,6 @@ process_file() {
return 0
fi
# Calculate additional print arguments.
forward_file_name "$file"
# Print the formatted file.
if [[ "$formatter" = "none" ]]; then
if [[ -z "$OPT_LANGUAGE" ]]; then
@ -327,7 +184,7 @@ process_file() {
# shellcheck disable=SC2094 disable=SC2181
if [[ "$file" = "-" ]]; then
data_raw="$(cat -)"
data_formatted="$("formatter_${formatter}_process" "STDIN${fext}" 2>/dev/null <<<"$data_raw")"
data_formatted="$("formatter_${formatter}_process" "$file" 2>/dev/null <<<"$data_raw")"
if [[ $? -ne 0 ]]; then
print_warning "'STDIN': Unable to format with '%s'" "$formatter"
@ -348,19 +205,6 @@ process_file() {
return $?
}
# -----------------------------------------------------------------------------
# Version-Specific Features:
# -----------------------------------------------------------------------------
BAT_VERSION="$(bat_version)"
forward_file_name() { :; }
if version_compare "$BAT_VERSION" -ge "0.14"; then
forward_file_name() {
PRINT_ARGS+=("--file-name" "$1")
}
fi
# -----------------------------------------------------------------------------
# Main:
# -----------------------------------------------------------------------------
@ -369,8 +213,6 @@ OPT_LANGUAGE=
FILES=()
DEBUG_PRINT_FORMATTER=false
TERMINAL_WIDTH="$(term_width)"
# Parse arguments.
while shiftopt; do
case "$OPT" in
@ -383,14 +225,9 @@ while shiftopt; do
# Debug options
--debug:formatter) DEBUG_PRINT_FORMATTER=true ;;
# Read from stdin
-) FILES+=("-") ;;
# bat options
-*) {
if [[ -n "$OPT_VAL" ]]; then BAT_ARGS+=("$OPT=$OPT_VAL");
else BAT_ARGS+=("$OPT");
fi
BAT_ARGS+=("$OPT=$OPT_VAL")
} ;;
# Files

View file

@ -23,19 +23,13 @@ export TEST_SHIM_PATH="${HERE}/test/shim"
export SNAPSHOT_DIR="${HERE}/test/snapshot"
OPT_ARGV=()
SHIFTOPT_SHORT_OPTIONS="PASS"
while shiftopt; do
case "$OPT" in
--compiled)
TEST_ENV_BIN_DIR="${HERE}/bin"
TEST_ENV_BIN_SUFFIX=""
;;
*)
if [[ "$OPT_VAL" ]]; then
OPT_ARGV+=("${OPT}=${OPT_VAL}")
else
OPT_ARGV+=("$OPT")
fi ;;
*) OPT_ARGV+=("$OPT") ;;
esac
done

Binary file not shown.

View file

@ -1,3 +0,0 @@
batdiff() {
"${BIN_DIR}/batdiff${BIN_SUFFIX}" "$@" || return $?
}

View file

@ -1,7 +0,0 @@
batpipe() {
"$(batpipe_path)" "$@" || return $?
}
batpipe_path() {
echo "${BIN_DIR}/batpipe${BIN_SUFFIX}"
}

View file

@ -1,15 +1,9 @@
#!/usr/bin/env bash
exec 3<&0
# Find the real bat.
HERE="$(cd "$(dirname "$0")" && pwd)"
while read -d ':' -r dir; do
if [[ "$dir" == "$HERE" || -z "$dir" ]]; then continue; fi
if [[ "$dir" == "$HERE" ]]; then continue; fi
if [[ -f "${dir}/bat" ]]; then
exec "${dir}/bat" "$@" --no-config --theme="Monokai Extended" <&3
exec "${dir}/bat" "$@" --theme="Monokai Extended"
fi
done <<<"$PATH:"
# Print error and exit.
echo "bat was not found on \$PATH" 1>&2
exit 127
done <<<"$PATH"

View file

@ -1,4 +0,0 @@
#!/usr/bin/env bash
# Run bash, but with executable name as `fish`.
exec -a "${SHIM_ARGV0:-fish}" bash "$@"
exit $?

View file

@ -12,10 +12,4 @@ EOF
exit 0
fi
FILES=()
while [[ $# -gt 0 ]]; do
-*) : ;;
*) FILES+=("$1")
done
cat "${FILES[@]}"
cat "$1"

View file

@ -1,7 +0,0 @@
#!/usr/bin/env bash
# Run bash, but with executable name as `nu`.
#
# Spawn the process in the background and wait on it to ensure we keep
# the fake shell as a parent process.
exec -a "${SHIM_ARGV0:-nu}" bash "$@"
exit $?

View file

@ -1,4 +1,5 @@
#!/usr/bin/env bash
case "$1" in
size) echo "20 80" ;;
cols) echo 80 ;;
lines) echo 22 ;;
esac

View file

@ -1,3 +0,0 @@
batdiff
Copyright (C) 2019-2021 eth-p | MIT License
https://github.com/eth-p/bat-extras

View file

@ -1,107 +0,0 @@
Quickly search through and highlight files using ripgrep.
Search through files or directories looking for matching regular expressions (or fixed strings with -F), and print the output using bat for an easy and syntax-highlighted experience.
Usage: batgrep [OPTIONS] PATTERN [PATH...]
Arguments:
[OPTIONS]
See Options below
PATTERN
Pattern passed to ripgrep
[PATH...]
Path(s) to search
Options:
-i, --ignore-case:
Use case insensitive searching.
-s, --case-sensitive:
Use case sensitive searching.
-S, --smart-case:
Use smart case searching
-A, --after-context=[LINES]:
Display the next n lines after a matched line.
-B, --before-context=[LINES]:
Display the previous n lines before a matched line.
-C, --context=[LINES]:
A combination of --after-context and --before-context
-p, --search-pattern:
Tell pager to search for PATTERN. Currently supported pagers: less.
--no-follow:
Do not follow symlinks
--no-snip:
Do not show the snip decoration
This is automatically enabled when --context=0 or when bat --version is less than 0.12.x
--no-highlight:
Do not highlight matching lines.
This is automatically enabled when --context=0.
--color:
Force color output.
--no-color:
Force disable color output.
--paging=["never"/"always"]:
Enable/disable paging.
--pager=[PAGER]:
Specify the pager to use.
--terminal-width=[COLS]:
Generate output for the specified terminal width.
--no-separator:
Disable printing separator between files.
--rga:
Use ripgrep-all instead of ripgrep.
Options passed directly to ripgrep:
-F, --fixed-strings
-U, --multiline
-P, --pcre2
-z, --search-zip
-w, --word-regexp
--one-file-system
--multiline-dotall
--ignore, --no-ignore
--crlf, --no-crlf
--hidden, --no-hidden
-E --encoding:
This is unsupported by bat, and may cause issues when trying to display unsupported encodings.
-g, --glob
-t, --type
-T, --type-not
-m, --max-count
--max-depth
--iglob
--ignore-file

View file

@ -1,8 +1,8 @@
────────────────────────────────────────────────────────────────────────────────
File: file.txt
 1 cat 
 2 dog
 3 car 
 4 frog
 5 fox
 1 cat 
 2 dog
 3 car 
 4 frog
 5 fox
────────────────────────────────────────────────────────────────────────────────

View file

@ -1,6 +0,0 @@
File: file.txt
 1 cat 
 2 dog
 3 car 
 4 frog
 5 fox

View file

@ -1,7 +0,0 @@
────────────────────────────────────────────────────────────────────────────────
cat 
dog
car 
frog
fox
────────────────────────────────────────────────────────────────────────────────

View file

@ -1,7 +0,0 @@
────────────────────────────────────────────────────────────────────────────────
cat
dog
car
frog
fox
────────────────────────────────────────────────────────────────────────────────

View file

@ -1,3 +1,3 @@
batgrep
Copyright (C) 2019-2021 eth-p | MIT License
Copyright (C) 2019-2020 eth-p | MIT License
https://github.com/eth-p/bat-extras

View file

@ -1,3 +1,3 @@
batman
Copyright (C) 2019-2021 eth-p | MIT License
Copyright (C) 2019-2020 eth-p | MIT License
https://github.com/eth-p/bat-extras

View file

@ -1,26 +0,0 @@
───────┬────────────────────────────────
│ File: file.txt
───────┼────────────────────────────────
 1 │ cat
 2 │ dog
 3 │ car
 4 │ frog
 5 │ fox
 6 │ clocks
 7 │ bash
 8 │ $300
 9 │ ^$!@
───────┴────────────────────────────────
───────┬────────────────────────────────────────────────────
│ File: file.txt
───────┼────────────────────────────────────────────────────
 1 │ cat
 2 │ dog
 3 │ car
 4 │ frog
 5 │ fox
 6 │ clocks
 7 │ bash
 8 │ $300
 9 │ ^$!@
───────┴────────────────────────────────────────────────────

View file

@ -1,4 +0,0 @@
echo Hello, test world!
if true; then
! true
fi

View file

@ -1,2 +0,0 @@
Usage: batwatch --file [--watcher entr|poll][--[no-]clear] <file> [<file> ...]
batwatch --command [-n<interval>] <command> [<arg> ...]

View file

@ -1,3 +1,3 @@
batwatch
Copyright (C) 2019-2021 eth-p | MIT License
Copyright (C) 2019-2020 eth-p | MIT License
https://github.com/eth-p/bat-extras

View file

@ -1,3 +1,3 @@
prettybat
Copyright (C) 2019-2021 eth-p | MIT License
Copyright (C) 2019-2020 eth-p | MIT License
https://github.com/eth-p/bat-extras

View file

@ -1,12 +0,0 @@
setup() {
use_shim 'batdiff'
}
test:version() {
description "Test 'batdiff --version'"
snapshot stdout
snapshot stderr
batdiff --version | awk 'FNR <= 1 { print $1 }'
batdiff --version | awk 'p{print} /^$/ { p=1 }'
}

View file

@ -2,8 +2,6 @@ HAS_RIPGREP=false
setup() {
use_shim 'batgrep'
unset BAT_STYLE
if command -v rg &>/dev/null; then
HAS_RIPGREP=true
@ -16,21 +14,12 @@ require_rg() {
fi
}
test:help() {
description "Test 'batgrep --help'"
snapshot stdout
batgrep --help
assert batgrep --help
batgrep --help | grep -q 'Usage'
}
test:version() {
description "Test 'batgrep --version'"
snapshot stdout
snapshot stderr
batgrep --version | awk 'FNR <= 1 { print $1 }'
batgrep --version | head -n1 | cut -d' ' -f1
batgrep --version | awk 'p{print} /^$/ { p=1 }'
}
@ -103,43 +92,3 @@ test:option_context() {
batgrep -C 0 '\$' file.txt
}
test:search_from_stdin() {
description "Should be able to search through stdin."
snapshot stdout
snapshot stderr
require_rg
cat file.txt | batgrep "^ca"
}
test:respects_bat_style() {
description "Should respect the BAT_STYLE variable."
snapshot stdout
snapshot stderr
require_rg
BAT_STYLE="grid" batgrep "ca" file.txt --color=always
}
test:output_without_separator() {
description "Snapshot test for output without separator"
snapshot stdout
snapshot stderr
require_rg
batgrep "ca" file.txt --no-separator --color=always
}
test:sanity_rg_works() {
description "Ensure the ripgrep executable works"
require_rg
rg --version
rg "ca" file.txt | grep "ca" || fail "Ripgrep executable not working."
}

View file

@ -7,6 +7,6 @@ test:version() {
snapshot stdout
snapshot stderr
batman --version | awk 'FNR <= 1 { print $1 }'
batman --version | head -n1 | cut -d' ' -f1
batman --version | awk 'p{print} /^$/ { p=1 }'
}

View file

@ -1,61 +0,0 @@
setup() {
use_shim 'batpipe'
}
test:detected_bash_shell() {
description "Test it can detect a bash shell."
command -v "bash" &>/dev/null || skip "Test requires bash shell."
output="$(SHELL="bash" bash --login -c "{ \"$(batpipe_path)\"; }")" # This hack prevents bash from exec()'ing itself.
grep '^LESSOPEN=' <<< "$output" >/dev/null || fail "Detected the wrong shell for bash."
}
test:detected_fish_shell() {
description "Test it can detect a fish shell."
# Note: We don't use bash's `-c` option when testing with a fake fish shell.
# Bash `-c` will automatically exec() into the last process, which loses the
# argv0 we intentionally named after a different shell.
# Test detection via `*sh -l` parent process.
output="$(printf "%q" "$(batpipe_path)" | fish -l)"
grep '^set -x' <<< "$output" >/dev/null || fail 'Detected wrong shell when checking parent process args.'
# Test detection via hypen-prefixed parent process.
output="$(printf "%q" "$(batpipe_path)" | SHIM_ARGV0='-fish' fish)"
grep '^set -x' <<< "$output" >/dev/null || fail 'Detected wrong shell when checking parent process.'
}
test:detected_nu_shell() {
description "Test it can detect a nushell shell."
# Note: We don't use bash's `-c` option when testing with a fake nu shell.
# Bash `-c` will automatically exec() into the last process, which loses the
# argv0 we intentionally named after a different shell.
# Test detection via `*sh -l` parent process.
output="$(printf "%q" "$(batpipe_path)" | nu -l)"
grep '^\$env' <<< "$output" >/dev/null || fail 'Detected wrong shell when checking parent process args.'
# Test detection via hypen-prefixed parent process.
output="$(printf "%q" "$(batpipe_path)" | SHIM_ARGV0='-nu' nu -l)"
grep '^\$env' <<< "$output" >/dev/null || fail 'Detected wrong shell when checking parent process.'
}
test:viewer_gzip() {
description "Test it can view .gz files."
command -v "gunzip" &>/dev/null || skip "Test requires gunzip."
assert_equal "$(batpipe compressed.txt.gz)" "OK"
}
test:batpipe_term_width() {
description "Test support for BATPIPE_TERM_WIDTH"
snapshot STDOUT
export BATPIPE=color
export BATPIPE_DEBUG_PARENT_EXECUTABLE=less
BATPIPE_TERM_WIDTH=40 batpipe file.txt
BATPIPE_TERM_WIDTH=-20 batpipe file.txt
}

View file

@ -1,5 +1,3 @@
#!/bin/bash
setup() {
use_shim 'batwatch'
}
@ -9,23 +7,6 @@ test:version() {
snapshot stdout
snapshot stderr
batwatch --version | awk 'FNR <= 1 { print $1 }'
batwatch --version | head -n1 | cut -d' ' -f1
batwatch --version | awk 'p{print} /^$/ { p=1 }'
}
test:help() {
description "Test 'batwatch --help'"
snapshot stdout
batwatch --help
assert batwatch --help
batwatch --help | grep -q 'Usage'
}
test:displayed() {
description "Test 'batwatch <file>': <file> should be displayed'"
snapshot stdout
batwatch --no-clear --watcher poll file.sh <<< "q"
batwatch --watcher poll file.sh <<< "q" | grep -q "Hello"
}

View file

@ -1,98 +0,0 @@
setup() {
source "${LIB}/dsl.sh"
}
# Expect functions.
expect_dsl_command() {
EXPECTED_DSL_ARGS=("$@")
CALLED="not called"
dsl_on_command() {
expect_equal "$# args" "${#EXPECTED_DSL_ARGS[@]} args"
CALLED="called"
local arg
local i=0
for arg in "$@"; do
expect_equal "$arg" "${EXPECTED_DSL_ARGS[$i]}"
((i++)) || true
done
}
dsl_parse
expect_equal "$CALLED" "called"
}
expect_dsl_option() {
EXPECTED_DSL_ARGS=("$@")
CALLED="not called"
dsl_on_option() {
expect_equal "$# args" "${#EXPECTED_DSL_ARGS[@]} args"
CALLED="called"
local arg
local i=0
for arg in "$@"; do
expect_equal "$arg" "${EXPECTED_DSL_ARGS[$i]}"
((i++)) || true
done
}
dsl_parse
expect_equal "$CALLED" "called"
}
# Stub methods.
dsl_on_command() {
:
}
dsl_on_command_commit() {
:
}
dsl_on_option() {
:
}
# Test cases.
test:parse_command() {
description "Parses a DSL command."
expect_dsl_command "my-command" <<-EOF
my-command
EOF
}
test:parse_simple_args() {
description "Parses a DSL command with simple args"
expect_dsl_command "my-command" "arg1" "arg2" "arg3" <<-EOF
my-command arg1 arg2 arg3
EOF
}
test:parse_quoted_args() {
description "Parses a DSL command with quoted args"
expect_dsl_command "my-command" "arg 1" "" "arg3" <<-EOF
my-command "arg 1" "" "arg3
EOF
}
test:parse_escaped_args() {
description "Parses a DSL command with escaped args"
# Note: Bash will escape the \\ into \. It's only doubled in this heredoc.
expect_dsl_command "my-command" "arg\"1" "arg 2" "arg\\3" <<-EOF
my-command arg\"1 arg\ 2 arg\\\\3
EOF
}
test:parse_option() {
description "Parses a DSL option with simple arguments"
expect_dsl_option "my-option" "1" "2" "3" <<-EOF
my-command
my-option 1 2 3
EOF
}

View file

@ -1,22 +1,16 @@
setup() {
set - --long-implicit implicit_value \
--long-explicit=explicit_value \
-i implicit \
-x=explicit \
-I1 group_implicit \
-X2=group_explicit \
positional_1 \
positional_2 \
--after-positional
set - pos1 \
--val1 for_val1 \
--val2=for_val2 \
pos2 \
--flag1 \
-v3=for_val3 \
-v4 for_val4 \
--flag2
source "${LIB}/print.sh"
source "${LIB}/opt.sh"
}
assert_opt_name() {
assert [ "$OPT" = "$1" ]
}
assert_opt_value() {
assert [ "$OPT_VAL" = "$1" ]
}
@ -25,12 +19,11 @@ assert_opt_valueless() {
assert [ -z "$OPT_VAL" ]
}
test:long() {
description "Parse long options."
while shiftopt; do
if [[ "$OPT" = "--long-implicit" ]]; then
if [[ "$OPT" = "--flag1" ]]; then
assert_opt_valueless
return
fi
@ -39,13 +32,13 @@ test:long() {
fail 'Failed to find option.'
}
test:long_implicit() {
test:long_value_implicit() {
description "Parse long options in '--long value' syntax."
while shiftopt; do
if [[ "$OPT" = "--long-implicit" ]]; then
if [[ "$OPT" = "--val1" ]]; then
shiftval
assert_opt_value 'implicit_value'
assert_opt_value 'for_val1'
return
fi
done
@ -53,14 +46,13 @@ test:long_implicit() {
fail 'Failed to find option.'
}
test:long_explicit() {
test:long_value_explicit() {
description "Parse long options in '--long=value' syntax."
while shiftopt; do
if [[ "$OPT" = "--long-explicit" ]]; then
assert_opt_value 'explicit_value'
if [[ "$OPT" = "--val2" ]]; then
shiftval
assert_opt_value 'explicit_value'
assert_opt_value 'for_val2'
return
fi
done
@ -68,40 +60,13 @@ test:long_explicit() {
fail 'Failed to find option.'
}
test:short_default() {
description "Parse short options in '-k' syntax."
while shiftopt; do
if [[ "$OPT" = "-i" ]]; then
assert_opt_valueless
return
fi
done
fail 'Failed to find option.'
}
test:short() {
description "Parse short options in '-k val' syntax."
while shiftopt; do
if [[ "$OPT" = "-i" ]]; then
shiftopt
assert_opt_valueless
return
fi
done
fail 'Failed to find option.'
}
test:short_implicit() {
test:short_value_implicit() {
description "Parse short options in '-k value' syntax."
while shiftopt; do
if [[ "$OPT" = "-i" ]]; then
if [[ "$OPT" = "-v4" ]]; then
shiftval
assert_opt_value 'implicit'
assert_opt_value 'for_val4'
return
fi
done
@ -109,341 +74,15 @@ test:short_implicit() {
fail 'Failed to find option.'
}
test:short_explicit() {
test:short_value_explicit() {
description "Parse short options in '-k=value' syntax."
while shiftopt; do
if [[ "$OPT" = "-x" ]]; then
assert_opt_value 'explicit'
shiftval
assert_opt_value 'explicit'
if [[ "$OPT" =~ ^-v3 ]]; then
assert_equal "$OPT" "-v3=for_val3"
return
fi
done
fail 'Failed to find option.'
}
test:short_default_mode() {
description "Ensure the default mode for '-abc' is VALUE."
assert_equal "$SHIFTOPT_SHORT_OPTIONS" "VALUE"
}
test:short_split_none() {
description "Parse short options in '-abc' syntax with SPLIT mode."
SHIFTOPT_SHORT_OPTIONS="SPLIT"
local found=0
while shiftopt; do
case "$OPT" in
"-I"|"-1")
assert_opt_valueless
((found++)) || true
;;
esac
done
assert_equal "$found" 2
}
test:short_split_implicit() {
description "Parse short options in '-abc val' syntax with SPLIT mode."
SHIFTOPT_SHORT_OPTIONS="SPLIT"
local found=0
while shiftopt; do
case "$OPT" in
"-I"|"-1")
assert_opt_valueless
shiftval
assert_opt_value "group_implicit"
((found++)) || true
;;
esac
done
assert_equal "$found" 2
}
test:short_split_explicit() {
description "Parse short options in '-abc=val' syntax with SPLIT mode."
SHIFTOPT_SHORT_OPTIONS="SPLIT"
local found=0
while shiftopt; do
case "$OPT" in
"-X"|"-2")
assert_opt_value "group_explicit"
((found++)) || true
;;
esac
done
assert_equal "$found" 2
}
test:short_pass_none() {
description "Parse short options in '-abc' syntax with PASS mode."
SHIFTOPT_SHORT_OPTIONS="PASS"
local found=0
while shiftopt; do
case "$OPT" in
"-I"|"-1") fail "Short group -I1 was split." ;;
"-I1")
assert_opt_valueless
((found++)) || true
;;
esac
done
assert_equal "$found" 1
}
test:short_pass_implicit() {
description "Parse short options in '-abc val' syntax with PASS mode."
SHIFTOPT_SHORT_OPTIONS="PASS"
local found=0
while shiftopt; do
case "$OPT" in
"-I"|"-1") fail "Short group -I1 was split." ;;
"-I1")
assert_opt_valueless
shiftval
assert_opt_value "group_implicit"
((found++)) || true
;;
esac
done
assert_equal "$found" 1
}
test:short_pass_explicit() {
description "Parse short options in '-abc=val' syntax with PASS mode."
SHIFTOPT_SHORT_OPTIONS="PASS"
local found=0
while shiftopt; do
case "$OPT" in
"-X"|"-2") fail "Short group -X2 was split." ;;
"-X2")
assert_opt_value "group_explicit"
((found++)) || true
;;
esac
done
assert_equal "$found" 1
}
test:short_conv_none() {
description "Parse short options in '-abc' syntax with CONV mode."
SHIFTOPT_SHORT_OPTIONS="CONV"
local found=0
while shiftopt; do
case "$OPT" in
"-I"|"-1") fail "Short group -I1 was split." ;;
"-I1") fail "Short group -I1 was not converted." ;;
"--I1")
assert_opt_valueless
((found++)) || true
;;
esac
done
assert_equal "$found" 1
}
test:short_conv_implicit() {
description "Parse short options in '-abc val' syntax with CONV mode."
SHIFTOPT_SHORT_OPTIONS="CONV"
local found=0
while shiftopt; do
case "$OPT" in
"-I"|"-1") fail "Short group -I1 was split." ;;
"-I1") fail "Short group -I1 was not converted." ;;
"--I1")
assert_opt_valueless
shiftval
assert_opt_value "group_implicit"
((found++)) || true
;;
esac
done
assert_equal "$found" 1
}
test:short_conv_explicit() {
description "Parse short options in '-abc=val' syntax with CONV mode."
SHIFTOPT_SHORT_OPTIONS="CONV"
local found=0
while shiftopt; do
case "$OPT" in
"-X"|"-2") fail "Short group -X2 was split." ;;
"-X2") fail "Short group -X2 was not converted." ;;
"--X2")
assert_opt_value "group_explicit"
((found++)) || true
;;
esac
done
assert_equal "$found" 1
}
test:short_value_none() {
description "Parse short options in '-abc' syntax with VALUE mode."
SHIFTOPT_SHORT_OPTIONS="VALUE"
local found=0
while shiftopt; do
case "$OPT" in
"-I1") fail "Short group -I1 was not truncated." ;;
"-I")
assert_opt_value "1"
((found++)) || true
;;
esac
done
assert_equal "$found" 1
}
test:short_value_implicit() {
description "Parse short options in '-abc val' syntax with VALUE mode."
SHIFTOPT_SHORT_OPTIONS="VALUE"
local found=0
while shiftopt; do
case "$OPT" in
"-I1") fail "Short group -I1 was not truncated." ;;
"-I")
shiftval
assert_opt_value "1"
((found++)) || true
;;
esac
done
assert_equal "$found" 1
}
test:short_value_explicit() {
description "Parse short options in '-abc=val' syntax with VALUE mode."
SHIFTOPT_SHORT_OPTIONS="VALUE"
local found=0
while shiftopt; do
case "$OPT" in
"-X2") fail "Short group -X2 was not truncated." ;;
"-X")
assert_opt_value "2=group_explicit"
((found++)) || true
;;
esac
done
assert_equal "$found" 1
}
test:hook() {
description "Option hooks."
SHIFTOPT_HOOKS=("example_hook")
found=false
example_hook() {
if [[ "$OPT" = "--long-implicit" ]]; then
found=true
return 0
fi
return 1
}
while shiftopt; do
if [[ "$OPT" = "--long-implicit" ]]; then
fail "Option was not filtered by hook."
fi
done
if ! "$found"; then
fail "Option was not found by hook."
fi
}
test:fn_setargs() {
description "Function setargs."
setargs "--setarg=true"
shiftopt || true
shiftval
assert_opt_name "--setarg"
assert_opt_value "true"
}
test:fn_resetargs() {
description "Function resetargs."
setargs "--setarg=true"
resetargs
shiftopt || true
assert_opt_name "--long-implicit"
}
# shellcheck disable=SC2154
test:fn_getargs() {
description "Function getargs."
setargs "--one=two" "three" "--four"
shiftopt
getargs args
assert_equal 2 "${#args[@]}"
assert_equal "three" "${args[0]}"
assert_equal "--four" "${args[1]}"
# Ensure getargs doesn't remove remaining arguments.
shiftopt
assert_opt_name "three"
# Ensure it doesn't set an empty string.
args=(one two)
setargs
getargs args
assert_equal 0 "${#args[@]}"
}
test:fn_getargs_append() {
description "Function getargs -a."
setargs "--two=three" "four"
args=(zero one)
getargs -a args
assert_equal 4 "${#args[@]}"
assert_equal "zero" "${args[0]}"
assert_equal "one" "${args[1]}"
assert_equal "--two=three" "${args[2]}"
assert_equal "four" "${args[3]}"
# Ensure it doesn't append empty strings.
args=()
setargs "zero" "one"
getargs -a args
assert_equal 2 "${#args[@]}"
args=(zero one)
setargs
getargs -a args
assert_equal 2 "${#args[@]}"
}

View file

@ -6,119 +6,69 @@ use_pager() {
unset BAT_PAGER
export PAGER="$1"
_configure_pager
_detect_pager
}
use_no_pager() {
unset BAT_PAGER
unset PAGER
SCRIPT_PAGER_CMD=("$PAGER")
SCRIPT_PAGER_ARGS=()
_detect_pager --force
_configure_pager
_detect_pager
}
use_bat_pager() {
unset PAGER
export BAT_PAGER="$1"
SCRIPT_PAGER_CMD=($BAT_PAGER)
SCRIPT_PAGER_ARGS=()
_detect_pager --force
_configure_pager
_detect_pager
}
test:less_detection() {
description "Identify less"
(use_pager "less" && expect_equal "$(pager_name)" "less")
(use_pager "less_but_renamed" && expect_equal "$(pager_name)" "less")
(use_pager "stty" && expect_equal "$(pager_name)" "stty")
}
test:bat_detection() {
description "Ensure bat is replaced with less as pager"
use_pager "bat"
expect_equal "$(pager_name)" "less"
expect array_contains "-R" in "${SCRIPT_PAGER_CMD[@]}"
use_pager "less" && expect_equal "$(pager_name)" "less"
use_pager "less_but_renamed" && expect_equal "$(pager_name)" "less"
use_pager "tput" && expect_equal "$(pager_name)" "tput"
}
test:less_version() {
description "Identify less version"
(
export MOCK_LESS_VERSION=473
use_pager "less" && expect_equal "$(pager_version)" "473"
)
export MOCK_LESS_VERSION=473
use_pager "less" && expect_equal "$(pager_version)" "473"
(
export MOCK_LESS_VERSION=551
use_pager "less" && expect_equal "$(pager_version)" "551"
)
export MOCK_LESS_VERSION=551
use_pager "less" && expect_equal "$(pager_version)" "551"
}
test:less_args() {
description "Automatically select appropriate less args"
# When pager is "less".
(
export MOCK_LESS_VERSION=473
use_pager "less"
expect array_contains "-R" in "${SCRIPT_PAGER_CMD[@]}"
expect array_contains "--quit-if-one-screen" in "${SCRIPT_PAGER_CMD[@]}"
expect array_contains "--no-init" in "${SCRIPT_PAGER_CMD[@]}"
)
export MOCK_LESS_VERSION=473
use_pager "less"
expect array_contains "-R" in "${SCRIPT_PAGER_ARGS[@]}"
expect array_contains "--quit-if-one-screen" in "${SCRIPT_PAGER_ARGS[@]}"
expect array_contains "--no-init" in "${SCRIPT_PAGER_ARGS[@]}"
# When pager is equivalent of "less".
(
export MOCK_LESS_VERSION=551
use_pager "less"
expect array_contains "-R" in "${SCRIPT_PAGER_CMD[@]}"
expect array_contains "--quit-if-one-screen" in "${SCRIPT_PAGER_CMD[@]}"
expect ! array_contains "--no-init" in "${SCRIPT_PAGER_CMD[@]}"
)
}
test:less_args_not_less() {
description "Don't give non-less pagers the less args"
export MOCK_LESS_VERSION=551
use_pager "less"
expect array_contains "-R" in "${SCRIPT_PAGER_ARGS[@]}"
expect array_contains "--quit-if-one-screen" in "${SCRIPT_PAGER_ARGS[@]}"
expect ! array_contains "--no-init" in "${SCRIPT_PAGER_ARGS[@]}"
use_pager "not_less"
echo "${SCRIPT_PAGER_CMD[@]}"
expect_equal "${#SCRIPT_PAGER_CMD[@]}" 1
expect_equal "${#SCRIPT_PAGER_ARGS[@]}" 0
}
test:env_bat_pager() {
description "Check that BAT_PAGER is being used"
use_bat_pager "not_less"
expect_equal "${SCRIPT_PAGER_CMD[0]}" "not_less"
expect_equal "$SCRIPT_PAGER_CMD" "not_less"
use_bat_pager "not_less but not_more"
expect_equal "${SCRIPT_PAGER_CMD[0]}" "not_less"
expect_equal "$SCRIPT_PAGER_CMD" "not_less"
expect_equal "${SCRIPT_PAGER_CMD[1]}" "but"
expect_equal "${SCRIPT_PAGER_CMD[2]}" "not_more"
}
test:env_no_pager() {
description "Check that no PAGER or BAT_PAGER defaults to less"
use_no_pager
expect_equal "${SCRIPT_PAGER_CMD[0]}" "less"
expect array_contains "-R" in "${SCRIPT_PAGER_CMD[@]}"
}
test:args_copied_from_pager() {
description "Check that the pager args are correct with PAGER."
use_pager "less --some-argument"
expect_equal "${SCRIPT_PAGER_CMD[0]}" "less"
expect_not_equal "${SCRIPT_PAGER_CMD[1]}" "--some-argument"
}
test:args_copied_from_bat_pager() {
description "Check that the pager args are correct with PAGER."
use_bat_pager "less --some-argument"
assert_equal "${#SCRIPT_PAGER_CMD[@]}" 2
expect_equal "${SCRIPT_PAGER_CMD[0]}" "less"
expect_equal "${SCRIPT_PAGER_CMD[1]}" "--some-argument"
}

View file

@ -1,74 +0,0 @@
# This test suite is meant to check the performance impact of loading the library scripts used in bat-extras.
# Whether or not it completes successfully doesn't matter.
# Test cases.
test:perf_baseline() {
description "The baseline for test execution time"
exit 0
}
test:perf_lib_constants() {
description "Record how long it takes to load the constants.sh library"
source "${LIB}/constants.sh"
exit 0
}
test:perf_lib_dsl() {
description "Record how long it takes to load the dsl.sh library"
source "${LIB}/dsl.sh"
exit 0
}
test:perf_lib_opt() {
description "Record how long it takes to load the opt.sh library"
source "${LIB}/opt.sh"
exit 0
}
test:perf_lib_opt_hook_color() {
description "Record how long it takes to load the opt_hook_color.sh library"
source "${LIB}/opt_hook_color.sh"
exit 0
}
test:perf_lib_opt_hook_pager() {
description "Record how long it takes to load the opt_hook_pager.sh library"
source "${LIB}/opt_hook_pager.sh"
exit 0
}
test:perf_lib_opt_hook_version() {
description "Record how long it takes to load the opt_hook_version.sh library"
source "${LIB}/opt_hook_version.sh"
exit 0
}
test:perf_lib_opt_hook_width() {
description "Record how long it takes to load the opt_hook_width.sh library"
source "${LIB}/opt_hook_width.sh"
exit 0
}
test:perf_lib_pager() {
description "Record how long it takes to load the pager.sh library"
source "${LIB}/pager.sh"
exit 0
}
test:perf_lib_print() {
description "Record how long it takes to load the print.sh library"
source "${LIB}/print.sh"
exit 0
}
test:perf_lib_str() {
description "Record how long it takes to load the str.sh library"
source "${LIB}/str.sh"
exit 0
}
test:perf_lib_version() {
description "Record how long it takes to load the version.sh library"
source "${LIB}/version.sh"
exit 0
}

View file

@ -7,19 +7,6 @@ test:version() {
snapshot stdout
snapshot stderr
prettybat --version | awk 'FNR <= 1 { print $1 }'
prettybat --version | head -n1 | cut -d' ' -f1
prettybat --version | awk 'p{print} /^$/ { p=1 }'
}
test:read_from_pipe() {
description "Test 'prettybat -'"
assert_equal "ABC" "$(echo "ABC" | prettybat)"
assert_equal "ABC" "$(echo "ABC" | prettybat -)"
}
test:read_from_pipe_with_formatter() {
description "Test 'prettybat - -lsh'"
assert_equal "-: shfmt" "$(echo "" | prettybat - -lsh --debug:formatter)"
}

View file

@ -1 +1 @@
2024.08.24
2020.04.08