Compare commits

..

106 commits

Author SHA1 Message Date
Ethan P. 3860f0f148
test: Add tests for nu shell detection
Some checks failed
Test / Build (push) Has been cancelled
Test / Test (push) Has been cancelled
Test / Test_Consistency (push) Has been cancelled
Test / Test_Symlinks (push) Has been cancelled
2025-02-21 19:01:37 -08:00
Ethan P. 58e7f52717
test: Add more robust tests for shell detection 2025-02-21 18:55:25 -08:00
Ethan P. 86fd93e47e
test: Test shell detection with fake fish shell
Rather than using a real fish shell for tests, we're faking it so
package maintainers don't need to add multiple shells as build
dependencies.
2025-02-21 18:12:48 -08:00
Ethan P. a0d3a9bd63
batpipe: Add detection for nushell 2025-02-21 18:02:54 -08:00
jlkDE b977dffc54 test: Recreate snapshots with bat v0.25.0
Some checks are pending
Test / Build (push) Waiting to run
Test / Test (push) Waiting to run
Test / Test_Consistency (push) Blocked by required conditions
Test / Test_Symlinks (push) Blocked by required conditions
Due to theme detection using terminal-colorsaurus now
(https://github.com/sharkdp/bat/pull/2896) the line numbers and borders
are now colored in alignment with the foreground color.
2025-02-21 17:07:01 -08:00
Ethan P. 36c77c171c
Bump version to 2024.08.24
Some checks failed
Test / Build (push) Has been cancelled
Test / Test (push) Has been cancelled
Test / Test_Consistency (push) Has been cancelled
Test / Test_Symlinks (push) Has been cancelled
2024-08-24 14:25:19 -07:00
Ethan P. 6092fa5bad
prettybad: Have yq output tabs instead of spaces
It's a hack with `sed`, but it's better than nothing.
2024-08-24 14:22:18 -07:00
Ethan P. 1cffc910d5
docs: Mention yq as formatter in prettybat docs 2024-08-24 14:07:27 -07:00
Ethan P. 47cfb9ec73
prettybat: Use yq for YAML and JSON pretty formatting 2024-08-24 14:02:23 -07:00
Adrian Wilkins 32706dd581 Trim trailing whitespace
Otherwise `MANPAGER`'s trailing whitespace is escaped, which adds the next line of `export` as part of the command.

```bash
export MANPAGER=env\ BATMAN_IS_BEING_MANPAGER=yes\ bash\ /usr/local/bin/batman\
export MANROFFOPT=-c
```

and you get `cat: export: No such file or directory`
2024-08-24 13:50:25 -07:00
Ethan P. 01c2f9bb94
test: Ignore flags passed to shim less
This fixes #126.
2024-08-24 13:45:36 -07:00
HE7086 0acf65edde batdiff: Support filenames with special characters 2024-08-24 13:38:41 -07:00
Mehdi Rafee 92dcb78765 Allow --wrap option to be passed to bat 2024-08-24 13:36:36 -07:00
Maor Kadosh ac4f8710ba Batman unalias 2024-08-24 13:36:00 -07:00
Ethan P. aef5a424b4
Bump version to 2024.07.10 2024-06-10 12:56:58 -07:00
Ethan P. 3b3a8923a1
docs: Update banner 2024-06-10 12:56:16 -07:00
Ethan P. eb7ad6b21b
batman: Provide way to use BATMAN as MANPAGER 2024-06-10 12:55:10 -07:00
Ethan P. f5b8cfb5d7
Bump version to 2024.06.01 2024-06-01 15:33:06 -07:00
Ethan P. 38bacab368
prettybat: Don't add extra newlines to tsv for gnu 2024-06-01 15:30:30 -07:00
Ethan P. 043d2da964
prettybat: Add basic support for tsv files 2024-06-01 15:24:20 -07:00
Joe DiPilato 75c757817f remove comma after black formatter 2024-05-16 12:24:57 -07:00
Ethan P. c9e917e43a
docs: Clarify how to use batman instead of man 2024-02-12 15:47:01 -08:00
Ethan P. 6c8879f1d7
Bump version to 2024.02.12 2024-02-12 15:35:00 -08:00
Ethan P. 6bd35e5aed
ci: Fix downloading ripgrep dependency 2024-02-12 12:23:54 -08:00
Ethan P. 205e9c40e1
batman: Fix BAT_PAGER causing infinite recursion
Rather than replacing `BAT_PAGER` with `MANPAGER` every time batman is
used, only replace it when batman is called directly (and not used as
the MANPAGER).
2024-02-12 01:28:34 -08:00
Ethan P. 5011f0709e
batgrep: Add support for ripgrep-all
To use `ripgrep-all`, add the `--rga` flag when running batgrep.
2024-02-12 01:18:11 -08:00
Ethan P. 82911b5b7b
batman: Remove '-u' flag when calling sed
The `-u` flag is not available on busybox `sed`.
2024-02-12 01:04:40 -08:00
Felix Yan a427f6eeda Update Arch Linux package URL in README.md
The old URL returns 404 now.
2024-02-12 01:01:06 -08:00
Ethan P. 8460b39956
batman: Remove dependency on col program
This follows proposed solution (C) in
https://github.com/sharkdp/bat/issues/2568
2024-02-12 00:58:56 -08:00
Herby Gillot 3f259f1a07 docs: add MacPorts install instructions
https://ports.macports.org/port/bat-extras/
2023-12-13 10:29:58 -08:00
Noah Betzen bc534e9dd2 Add Elixir mix format support 2023-12-13 10:26:22 -08:00
Ethan P. a686d6742c
ci: Only run release workflow on tag push 2023-09-22 11:32:06 -07:00
Ethan P. d5ea485a0b
ci: Update deprecated Actions commands 2023-09-22 11:13:07 -07:00
Ethan P. c4b2a12f73
Bump version to 2023.09.19 2023-09-19 10:57:28 -07:00
Ethan P. f192b7a7d8
batman: Split short options 2023-09-19 10:46:46 -07:00
Ethan P. c29dfb136a
batdiff: Show error when not in git repo
This is instead of allowing `git` to dump its own warning to the
console.
2023-09-13 11:53:49 -07:00
Ethan P. 2e1e768559
batdiff: Support filtering diffs by directory
This brings parity between using `delta` and `bat --diff`.
Previously, only `delta` supported this use case.
2023-09-13 11:15:20 -07:00
Ethan P. 4039859b62
batdiff: Check delta version for --hunk-style
This adds compatibility for older `delta` versions.
2023-09-13 11:06:11 -07:00
Ethan P. 6f579b1d45
batman: Document issues with non-default themes 2023-09-13 10:54:46 -07:00
Bill Mill c26f22c768 fix typos in batgrep help 2023-09-13 10:41:31 -07:00
Ethan P. 895b4ee0ce
Merge branch 'compressed-tar' 2023-09-13 10:35:51 -07:00
Ethan P. ba984f470e
Merge branch 'master' into compressed-tar 2023-09-13 10:33:26 -07:00
David Gus Shields 162c21d012 readd exa conditionally 2023-09-13 10:29:14 -07:00
David Gus Shields d96fc92165 change all references to exa to eza 2023-09-13 10:29:14 -07:00
Adam Spiers 85aaf4875d batpipe: Add support for gzipped and bzipped tar files
This adds support for *.{tgz,tar.gz,tbz,tar.bz2} files.
2023-08-03 17:01:26 +01:00
Adam Spiers 4c220a84a3 batpipe: Extract batpipe_archive_header() helper function 2023-08-03 16:49:53 +01:00
Ethan P ceaac1b917
ci: Remove wait job 2023-06-15 17:15:14 -07:00
Rex Ackermann 03549d651a build: Add termux support
sets the OPT_PREFIX to /data/data/com.termux/files/usr if termux is detected.
2023-06-15 16:56:16 -07:00
Bill Mill f47d9c4138 add a test 2023-06-15 16:54:45 -07:00
Bill Mill 762ac557f4 use hook_help 2023-06-15 16:54:45 -07:00
Bill Mill 15876936ed add --help to batgrep 2023-06-15 16:54:45 -07:00
Ethan P 94fbeb5b27
ci: Tag-to-release 2023-06-15 16:42:49 -07:00
Ethan P d0738b7289
dev: Allow release script to be used as lib 2023-06-15 16:16:19 -07:00
Ethan P 48fe8875f7
build: Remove end-of-line whitespace 2023-06-15 16:15:42 -07:00
Ethan P 5ac224bc87
build: A way to get data from build.sh 2023-06-15 16:15:41 -07:00
Ethan P 974073fc72
build: Add option to print source files 2023-06-15 16:15:40 -07:00
Ethan P 7aa73f8989
batdiff: Add '--staged' flag 2023-05-15 20:31:48 -07:00
Ethan P 7107e1603f
build: Add support for more alt executables 2023-05-15 19:50:24 -07:00
Ethan P 3e7041da7b
test: Update test framework 2023-05-15 19:31:29 -07:00
Ethan P 14f36f51b7
batman: Use self as MANPAGER
With certain implementations of `man`, the pager is called with execve
and naiive argument splitting instead of going through the shell.

This change uses the script as the pager itself, working around
the issue.
2023-05-15 19:03:00 -07:00
Ethan P e609d1a2c5
prettybat: Fix short flags passed to bat
Fixes #87.
2023-03-24 11:01:56 -07:00
Ethan P daf888f5ed
docs: Update install instructions for brew 2023-03-21 02:10:41 -07:00
Ethan P bea36be397
Bump version to 2023.03.21 2023-03-21 01:58:48 -07:00
Ethan P 673dc6fd75
batdiff: Update docs to add BATDIFF_USE_DELTA 2023-03-21 01:58:48 -07:00
Ethan P 97006f0f09
batman: Allow spaces in manual sections 2023-03-21 01:58:48 -07:00
Ethan P 287398acbc
batman: Fix parsing manual pages in fzf preview 2023-03-21 01:58:48 -07:00
Ethan P e53db161a7
batman: Only parse first manpage when selected 2023-03-21 01:58:48 -07:00
Ethan P f07a22d536
batpipe: Fix SELF variable being wrong when compressed 2023-03-21 01:58:48 -07:00
Ethan P ea604457ea
batpipe: Add BATPIPE_TERM_WIDTH 2023-03-21 01:58:47 -07:00
Ethan P 998e2ff1a9
batpipe: Fix opened-in-less detection 2023-03-21 01:58:47 -07:00
Ethan P 26111f9cee
ci: Fix installation of ripgrep for tests
The previous `find` command was grabbing the bash completions, rather
than the executable file. This should fix that.
2023-03-20 23:40:08 -07:00
Ethan P 76906ca014
test: Add test to ensure ripgrep works
This is intended to make debugging CI issues easier in the future.
2023-03-20 23:29:36 -07:00
Ethan P 428a78837d
ci: Fix errors with permissions 2023-03-20 23:04:43 -07:00
Ethan P 36786ba570
ci: Update action to fix warnings 2023-03-20 23:00:52 -07:00
Eoin Mcloughlin e9daffde45 Fix bug where some man pages would fail to open
Some man page sections contain alphabetical characters (e.g. 3bsd,
3perl, 3ssl.) This fixes a bug where selection of such pages would
not open any man page.
2023-03-20 22:45:15 -07:00
Alex Wood 3917100c6e Add instructions for bat-extras on Fedora 2023-03-20 22:42:24 -07:00
Amory Meltzer ☀ a4237efffd batwatch docs: Fix link to entr project 2023-03-20 22:36:49 -07:00
Ethan P 38014ba72d
docs: Add install steps for Gentoo (@ran-dall)
Thanks for maintaining the package and writing up the install instructions, @ran-dall!
2022-07-30 17:03:42 -07:00
Ethan P 3e2e8df594
docs: Update README about manual files
Manual files are now generated and installed by default.
2022-07-30 17:01:03 -07:00
Ethan P abda988cbd
build: Add manuals to '--install' 2022-07-29 16:51:59 -07:00
Ethan P 49f27efea2
docs: Update CI badge on README 2022-07-29 16:03:07 -07:00
Ethan P 2f2b33391a
ci: Remove CircleCI 2022-07-29 16:00:11 -07:00
Ethan P 90bb604d74
ci: Add GitHub Actions test workflow
This will eventually replace CircleCI.
2022-07-29 15:55:51 -07:00
Ethan P ad2d788d6c
build: Add more '--no-*' options 2022-07-29 14:01:55 -07:00
Ethan P 10d976da55
developer: Add '.vscode' to gitignore 2022-07-29 14:01:34 -07:00
Ethan P 62b8f74df2
docs: Add Arch Linux install instructions to README 2022-07-28 13:47:44 -07:00
Ethan P a011290c2f
Bump version to 2022.07.27 2022-07-27 13:30:27 -07:00
Trevor Leibert 20cc74e8b4 batman: Fix error when using fzf to pick manpages
- steps to reproduce error:
  - run batman with no arguments
  - pick any entry
  - script exits and man prints an error message similar to 'No manual entry for .k5identity  in section 5'

I speculated that this error was due to whitespace being attached to the second argument in the man call.
This commit fixes this error by piping the second argument through xargs to strip these spaces.

After applying this commit the script works as expected.
2022-07-21 19:37:56 -07:00
MithicSpirit 33c2c195dc batdiff: Fix line too long (>80 chars) 2022-07-21 19:37:08 -07:00
MithicSpirit 462df32d4c batdiff: Fix use of deprecated --hunk-style
Delta has deprecated --hunk-style in favor of its synonym
--hunk-header-decoration-style. See
https://github.com/dandavison/delta/releases/tag/0.12.0
2022-07-21 19:37:08 -07:00
Robert Irelan 69792e95da batgrep: handle -u flag to ripgrep 2022-07-21 19:35:55 -07:00
Konstantin Mikhailov 8ad00bd978 batgrep: add flag to disable separators between files 2021-08-29 13:49:25 -07:00
Ethan P 7803ecaba1
batpipe: Fix colors not showing up inside less 2021-08-21 16:19:17 -07:00
Ethan P d8532bb35a
mdroff: Remove <br/> from output 2021-08-21 16:03:05 -07:00
Ethan P ef0356a2d8
mdroff: Fix text being trimmed incorrectly 2021-08-21 16:02:48 -07:00
Ethan P 33e0c168e0
batgrep: Add support for '--' 2021-08-21 15:48:14 -07:00
Ethan P 82ac5c4391
batman: If fzf is installed, use batman to search through man pages 2021-08-21 15:22:29 -07:00
Ethan P f280553fb1
batman: Only set grid style if BAT_STYLE is not defined 2021-08-21 15:19:14 -07:00
Ethan P d989b0bfde
batwatch: Buffer to reduce flickering from redraw 2021-08-11 01:15:46 -07:00
Ethan P 20cdf5a998
lib: Fix no pager being used when PAGER and BAT_PAGER are unset (#57) 2021-04-13 12:08:37 -07:00
Ethan P 75d66fa352
batpipe: Fix incorrect colors in usage instructions 2021-04-10 12:31:52 -07:00
Ethan P a4a5f5a5fd
lib: Add test for 'less' as pager instead of 'bat' 2021-04-10 12:26:54 -07:00
Ethan P 4f0b961d12
lib: Use 'less' as pager instead of 'bat' 2021-04-10 12:24:41 -07:00
Ethan P cae57be87b
batgrep: Respect the BAT_STYLE environment variable (#54) 2021-04-09 01:12:36 -07:00
Ethan P a526c7927b
batgrep: Fix to work with rg 11 (#53) 2021-04-08 14:17:13 -07:00
Ethan P eee3156e8a
test: Fix fish being incorrectly detected as installed 2021-04-07 20:38:35 -07:00
46 changed files with 1862 additions and 367 deletions

View file

@ -1,107 +0,0 @@
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 openssh bash python3 diffutils ca-certificates curl shfmt
- run:
name: "Setup: Install test packages."
command: apk add util-linux coreutils fish
- 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"
manuals:
type: string
default: "false"
steps:
- run:
name: "Build"
command: ./build.sh --minify=<<parameters.minify>> --manuals=<<parameters.manuals>> --no-verify
jobs:
build:
executor: linux_alpine
steps:
- setup
- checkout
- build:
minify: "all"
manuals: "true"
- build:
minify: "all"
manuals: "true"
- store_artifacts:
path: bin
- store_artifacts:
path: man
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]

54
.github/actions/build/action.yaml vendored Normal file
View file

@ -0,0 +1,54 @@
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

@ -0,0 +1,31 @@
#!/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)"

31
.github/actions/build/create-zipball.sh vendored Normal file
View file

@ -0,0 +1,31 @@
#!/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

@ -0,0 +1,92 @@
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"

33
.github/actions/test/action.yaml vendored Normal file
View file

@ -0,0 +1,33 @@
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

45
.github/workflows/release.yaml vendored Normal file
View file

@ -0,0 +1,45 @@
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}}"

96
.github/workflows/test.yaml vendored Normal file
View file

@ -0,0 +1,96 @@
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'

1
.gitignore vendored
View file

@ -4,6 +4,7 @@
# Developer
.idea
.vscode
*.iml
# Project

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

View file

@ -48,14 +48,60 @@ Pretty-print source code and highlight it with `bat`.
### Homebrew
All of the `bat-extras` scripts can be installed with `brew install eth-p/software/bat-extras`.
All of the `bat-extras` scripts can be installed with `brew install bat-extras`.
If you would only like to install one of the scripts, you can use `brew install eth-p/software/bat-extras-[SCRIPT]` to install it.
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
```
&nbsp;
## Installation (![CircleCI](https://circleci.com/gh/eth-p/bat-extras.svg?style=svg))
## 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)
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.
@ -97,11 +143,10 @@ If you only want to install a single script, you can run the build process and c
**Manuals:** (EXPERIMENTAL)
**Manuals:**
You can specify `--manuals` to have the build script generate a `man` page for each of the markdown documentation files.
This is an experimental feature that uses a non-compliant Markdown "parser" written in Bash, and there is no guarantee
as for the quality of the generated manual pages.
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.
@ -110,6 +155,7 @@ as for the quality of the generated manual pages.
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:**

View file

@ -1,5 +1,5 @@
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2020 eth-p and contributors | MIT License
# bat-extras | Copyright (C) 2020-2024 eth-p and contributors | MIT License
#
# Repository: https://github.com/eth-p/bat-extras
# Issues: https://github.com/eth-p/bat-extras/issues

256
build.sh
View file

@ -1,19 +1,119 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2019 eth-p | MIT License
# 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)"
BIN="$HERE/bin"
SRC="$HERE/src"
MAN="$HERE/man"
MAN_SRC="$HERE/doc"
# -----------------------------------------------------------------------------
# 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)"
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
@ -120,12 +220,18 @@ generate_banner() {
#
# 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"
smsg "Reading${what}"
}
# Build step: preprocess
@ -218,7 +324,7 @@ step_write() {
smsg "Building"
}
# Build step: write
# Build step: write_install
# Optionally writes the output script to a file.
#
# Arguments:
@ -241,6 +347,47 @@ 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:
# -----------------------------------------------------------------------------
@ -369,12 +516,25 @@ OPT_MANUALS=true
OPT_INLINE=true
OPT_MINIFY="lib"
OPT_PREFIX="/usr/local"
OPT_BAT="$(basename "$EXECUTABLE_BAT")"
EXECUTABLE_BAT="$(basename -- "$EXECUTABLE_BAT")"
ALT_EXECS=()
BUILD_FILTER=()
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
@ -382,33 +542,47 @@ while shiftopt; do
--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; OPT_BAT="$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" ;;
--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")
;;
esac
done
if [[ "$OPT_BAT" != "bat" ]]; then
printc_msg "%{YELLOW}Building executable scripts with an alternate bat executable %{CLEAR}%s%{YELLOW}.%{CLEAR}\n" "$OPT_BAT"
if ! command -v "$OPT_BAT" &>/dev/null; then
printc_err "%{YELLOW}WARNING: Bash cannot execute the specified file.\n"
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"
fi
# shellcheck disable=SC2034
EXECUTABLE_BAT="$OPT_BAT"
printc_msg "\n"
fi
@ -441,6 +615,12 @@ fi
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
# -----------------------------------------------------------------------------
@ -449,56 +629,33 @@ fi
SOURCES=()
printc_msg "%{YELLOW}Preparing scripts...%{CLEAR}\n"
for file in "$SRC"/*.sh; do
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
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
done < <(batextras:get_source_paths)
if [[ "${#BUILD_FILTER[@]}" -gt 0 ]]; then
printf "\n"
printf "\n"
fi
# -----------------------------------------------------------------------------
# Build manuals.
if [[ "$OPT_MANUALS" = "true" ]]; then
source "${HERE}/mdroff.sh"
if ! [[ -d "$MAN" ]]; then
mkdir -p "$MAN"
fi
printc_msg "%{YELLOW}Building manuals...%{CLEAR}\n"
for source in "${SOURCES[@]}"; do
name="$(basename "$source" .sh)"
doc="${MAN_SRC}/${name}.md"
docout="${MAN}/${name}.1"
if ! [[ -f "$doc" ]]; then
continue
fi
printc_msg " %{YELLOW} %{MAGENTA}%s%{CLEAR}\n" "$(basename "$docout")"
(mdroff < "$doc" > "${MAN}/${name}.1")
done
printc_msg "\n"
fi
# -----------------------------------------------------------------------------
# Build files.
@ -521,6 +678,15 @@ 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
# -----------------------------------------------------------------------------

View file

@ -11,8 +11,13 @@ This script supports using [delta](https://github.com/dandavison/delta) as an al
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
@ -25,6 +30,7 @@ This script supports using [delta](https://github.com/dandavison/delta) as an al
| | `--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. |

View file

@ -31,6 +31,8 @@ Search through files or directories looking for matching regular expressions (or
| | `--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`. |
The following options are passed directly to ripgrep, and are not handled by this script.

View file

@ -4,13 +4,26 @@ 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
batman [SECTION] [ENTRY]
### As a Replacement for Man
With bash:
```bash
eval "$(batman --export-env)"
```
With fish:
```fish
batman --export-env | source
```
## Environment
@ -20,6 +33,21 @@ Gone are the days of losing your place while reading through monotone manual pag
## 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 $?
}
```
## Installation
@ -27,6 +55,18 @@ 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).

View file

@ -17,11 +17,19 @@ Like [lesspipe](https://github.com/wofr06/lesspipe), `batpipe` is designed to wo
## 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 | `exa`, `ls` |
| Directories | `eza`, `ls` |
| `*.tar`, `*.tar.gz` | `tar` |
| `*.zip`, `*.jar` | `unzip` |
| `*.gz` | `gunzip` |
@ -51,7 +59,7 @@ The `viewer_${viewer}_supports` function is called to determine if the external
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.

View file

@ -34,7 +34,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](http://entrproject.org/)
- [entr](https://eradman.com/entrproject/)
There is also a fallback `poll` watcher available.

View file

@ -21,22 +21,23 @@ 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 | [prettier](https://prettier.io/) |
| YAML | [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/) |
| 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) |

View file

@ -13,6 +13,7 @@ EXECUTABLE_BAT="$(command -v bat 2>/dev/null || command -v batcat 2>/dev/null ||
EXECUTABLE_GIT="git"
EXECUTABLE_DELTA="delta"
EXECUTABLE_RIPGREP="rg"
EXECUTABLE_FZF="fzf"
# Constants: Program
PROGRAM="$(basename "$0" .sh)"

View file

@ -12,6 +12,12 @@ 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)" ]]
@ -86,9 +92,13 @@ _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]}")"
@ -103,10 +113,14 @@ _detect_pager() {
# 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.
# Prefer the BAT_PAGER environment variable.
if [[ -n "${BAT_PAGER+x}" ]]; then
# [note]: This is intentional.
# shellcheck disable=SC2206
@ -114,6 +128,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

View file

@ -39,8 +39,11 @@ parent_shell() {
break
fi
# If the parent process has "*sh " followed by "-l", it's probably a login shell.
if [[ "$target_name" =~ sh\ .*-l ]]; then
# 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

View file

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

View file

@ -115,6 +115,16 @@ mdroff:emit:table_row() {
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"
@ -131,6 +141,11 @@ mdroff:emit() {
"mdroff:emit:${type}" "$data" "${@:3}"
}
mdroff:trim_right() {
# shellcheck disable=SC2001
sed 's/[[:space:]]*$//' <<< "$1"
}
mdroff:trim() {
sed 's/^[[:space:]]*//; s/[[:space:]]*$//' <<< "$1"
}
@ -147,7 +162,8 @@ mdroff:parseln() {
while [[ "${#buffer}" -gt 0 ]]; do
[[ "$buffer" =~ \*{1,3}|\`|\[([^\]]+)\]\(([^\)]+)\) ]] || {
printf "%s\n" "$(mdroff:trim "$buffer")"
mdroff:emit text "$(mdroff:trim_right "$buffer")"
mdroff:emit text $'\n'
return
}
@ -157,7 +173,7 @@ mdroff:parseln() {
before="${buffer:0:$pos}"
buffer="${buffer:$(($pos + ${#found}))}"
printf "%s" "$before"
mdroff:emit text "$before"
case "$found" in
'***')
if "$MDROFF_ATTR_STRONG" && "$MDROFF_ATTR_EMPHASIS"; then

View file

@ -1,10 +1,256 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# bat-extras | Copyright (C) 2019-2020 eth-p | MIT License
# 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
# -----------------------------------------------------------------------------
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")"
@ -14,22 +260,23 @@ 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="$HERE/bin"
OPT_DOC_DIR="$HERE/doc"
OPT_MAN_DIR="$HERE/man"
OPT_BIN_DIR="$(batextras:get_output_bin_directory)"
OPT_DOC_DIR="$(batextras:get_docs_directory)"
OPT_MAN_DIR="$(batextras:get_output_man_directory)"
while shiftopt; do
case "$OPT" in
--since)
shiftval
OPT_SINCE="$OPT_VAL"
if ! git rev-parse "$OPT_SINCE" &> /dev/null; then
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
@ -48,6 +295,10 @@ 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"
printc "%{RED}Expected: %{YELLOW}%s%{CLEAR}\n" "$VERSION_EXPECTED"
@ -55,6 +306,18 @@ if [[ "$VERSION" != "$VERSION_EXPECTED" ]] && ! "$OPT_BAD_IDEA"; then
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.
@ -75,110 +338,13 @@ printc "%{YELLOW}Building scripts...%{CLEAR}\n"
# Build package.
printc "%{YELLOW}Packaging artifacts...%{CLEAR}\n"
(
rm "$OPT_ARTIFACT"
cd "$(dirname "$OPT_BIN_DIR")"
zip -r "$OPT_ARTIFACT" "$(basename "$OPT_BIN_DIR")"
cd "$(dirname "$OPT_DOC_DIR")"
zip -ru "$OPT_ARTIFACT" "$(basename "$OPT_DOC_DIR")"
if [[ -d "$OPT_MAN_DIR" ]]; then
cd "$(dirname "$OPT_MAN_DIR")"
zip -ru "$OPT_ARTIFACT" "$(basename "$OPT_MAN_DIR")"
fi
)
batextras:create_package "$OPT_ARTIFACT"
printc "%{YELLOW}Package created as %{BLUE}%s%{YELLOW}.%{CLEAR}\n" "$OPT_ARTIFACT"
# -----------------------------------------------------------------------------
# Print template description package.
printc "%{YELLOW}Release description:%{CLEAR}\n"
# Get the commit hash.
COMMIT="$(git rev-parse HEAD)"
COMMIT_URL="https://github.com/eth-p/bat-extras/tree/${COMMIT}"
# 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 script names.
script_links=()
script_names=()
for script in "$SRC"/*.sh; do
script_name="$(basename "$script" .sh)"
script_names+=("$script_name")
script_links+=("[\`${script_name}\`](https://github.com/eth-p/bat-extras/blob/${COMMIT}/doc/${script_name}.md)")
done
script_pattern="$(printf 's/\\(%s\\)/`\\1`/;' "${script_names[@]}")"
SCRIPTS="$(printf "%s, " "${script_links[@]:0:$((${#script_links[@]} - 1))}")"
SCRIPTS="${SCRIPTS}and ${script_links[$((${#script_links[@]} - 1))]}"
# Get the changelog.
CHANGELOG_DEV=''
CHANGELOG=''
if [[ -n "$OPT_SINCE" ]]; then
ref="$(git rev-parse HEAD)"
end="$(git rev-parse "$OPT_SINCE")"
while [[ "$ref" != "$end" ]]; do
is_developer=false
ref_message="$(git show -s --format=%s "$ref")"
ref="$(git rev-parse "${ref}~1")"
if [[ "$ref_message" =~ ^([a-z-]+):[[:space:]]*(.*)$ ]]; then
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
# Switch to the correct changelog.
case "$affected_module" in
test | developer | ci | build) is_developer=true ;;
esac
fi
# Append to changelog.
if "$is_developer"; then
CHANGELOG_DEV="$CHANGELOG_DEV"$'\n'" - ${ref_message}"
else
CHANGELOG="$CHANGELOG"$'\n'" - ${ref_message}"
fi
done
fi
CHANGELOG="$(sed "$script_pattern" <<< "$CHANGELOG")"
CHANGELOG_DEV="$(sed "$script_pattern" <<< "$CHANGELOG_DEV")"
# Print the template.
sed '/\\$/{N;s/\\\n//;s/\n//p;}' <<- EOF
This contains the latest versions of $SCRIPTS as of commit [$(git rev-parse --short HEAD)]($COMMIT_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
batextras:generate_release_notes \
"$(batextras:get_previous_tag_name)" \
"$(batextras:get_current_commit)"

View file

@ -34,10 +34,13 @@ 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.
@ -48,6 +51,15 @@ 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.
@ -61,6 +73,7 @@ while shiftopt; do
# Script options
--all) OPT_ALL_CHANGES=true ;;
--staged) OPT_STAGED=true; GIT_ARGS+=("--staged") ;;
--delta) BATDIFF_USE_DELTA=true ;;
# ???
@ -77,7 +90,10 @@ done
# Append arguments for delta/bat.
BAT_ARGS+=("--terminal-width=${OPT_TERMINAL_WIDTH}" "--paging=never")
DELTA_ARGS+=("--width=${OPT_TERMINAL_WIDTH}" "--paging=never" "--hunk-style=plain")
DELTA_ARGS+=(
"--width=${OPT_TERMINAL_WIDTH}"
"--paging=never"
)
if "$OPT_COLOR"; then
BAT_ARGS+=("--color=always")
@ -91,9 +107,27 @@ if [[ -n "$OPT_TABS" ]]; then
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=("$@")
@ -103,11 +137,28 @@ print_bat_diff() {
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_BAT" --diff --diff-context="$OPT_CONTEXT" "${files[0]}" "${BAT_ARGS[@]}"
"$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 -U"$OPT_CONTEXT" "${files[0]}" | "$EXECUTABLE_BAT" --language=diff - "${BAT_ARGS[@]}"
"$EXECUTABLE_GIT" diff "${GIT_ARGS[@]}" "${files[0]}" | "$EXECUTABLE_BAT" --language=diff - "${BAT_ARGS[@]}"
fi
}
@ -121,7 +172,8 @@ print_delta_diff() {
fi
# Diff git file.
"$EXECUTABLE_GIT" diff -U"$OPT_CONTEXT" "${files[0]}" | "$EXECUTABLE_DELTA" "${DELTA_ARGS[@]}"
ensure_git_directory
"$EXECUTABLE_GIT" diff "${GIT_ARGS[@]}" "${files[0]}" | "$EXECUTABLE_DELTA" "${DELTA_ARGS[@]}"
}
if [[ "$BATDIFF_USE_DELTA" = "true" && "$SUPPORTS_DELTA" = "true" ]]; then
@ -158,12 +210,13 @@ fi
# -----------------------------------------------------------------------------
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 --name-only --diff-filter=d)
done < <("${EXECUTABLE_GIT}" diff "${GIT_ARGS[@]}" --name-only --diff-filter=d)
return
fi

View file

@ -5,6 +5,8 @@
# 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"
@ -12,6 +14,7 @@ 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"
@ -20,12 +23,128 @@ 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=""
@ -38,7 +157,8 @@ OPT_SNIP=""
OPT_HIGHLIGHT=true
OPT_SEARCH_PATTERN=false
OPT_FIXED_STRINGS=false
BAT_STYLE="header,numbers"
OPT_NO_SEPARATOR=false
BAT_STYLE="${BAT_STYLE:-header,numbers}"
# Set options based on the bat version.
if version_compare "$(bat_version)" -gt "0.12"; then
@ -63,11 +183,29 @@ if [[ -n "$RIPGREP_CONFIG_PATH" && -e "$RIPGREP_CONFIG_PATH" ]]; then
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" ;;
@ -117,6 +255,15 @@ 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:*) {
@ -130,6 +277,9 @@ while shiftopt; do
fi
} ;;
# --
--) getargs -a FILES; break ;;
# ???
-*) {
printc "%{RED}%s: unknown option '%s'%{CLEAR}\n" "$PROGRAM" "$OPT" 1>&2
@ -137,17 +287,15 @@ while shiftopt; do
} ;;
# Search
*) {
if [ -z "$PATTERN" ]; then
PATTERN="$OPT"
else
FILES+=("$OPT")
fi
} ;;
*) FILES+=("$OPT") ;;
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
@ -213,6 +361,12 @@ main() {
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
@ -228,17 +382,17 @@ main() {
--vimgrep \
"${RG_ARGS[@]}" \
--context 0 \
--no-context-separator \
--sort path \
-- \
"$PATTERN" \
"${FILES[@]}" \
)
if "$READ_FROM_STDIN"; then
"$EXECUTABLE_RIPGREP" "${COMMON_RG_ARGS[@]}" <<< "$STDIN_DATA"
"$RIPGREP" "${COMMON_RG_ARGS[@]}" <<< "$STDIN_DATA"
return $?
else
"$EXECUTABLE_RIPGREP" "${COMMON_RG_ARGS[@]}"
"$RIPGREP" "${COMMON_RG_ARGS[@]}"
return $?
fi
}
@ -247,7 +401,9 @@ main() {
[[ -z "$LAST_FILE" ]] && return 0
# Print the separator.
"$FIRST_PRINT" && echo "$SEP"
if ! "$NO_SEPARATOR"; then
"$FIRST_PRINT" && echo "$SEP"
fi
FIRST_PRINT=false
# Print the file.
@ -260,7 +416,9 @@ main() {
"$LAST_FILE"
# Print the separator.
echo "$SEP"
if ! "$NO_SEPARATOR"; then
echo "$SEP"
fi
}
do_print_from_file_or_stdin() {

View file

@ -6,8 +6,9 @@
# Issues: https://github.com/eth-p/bat-extras/issues
# -----------------------------------------------------------------------------
# shellcheck disable=SC1090 disable=SC2155
LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo ".")")/../lib" && pwd)"
if [[ -n "${MANPAGER}" ]]; then BAT_PAGER="$MANPAGER"; fi
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)"
source "${LIB}/constants.sh"
source "${LIB}/pager.sh"
source "${LIB}/print.sh"
@ -18,12 +19,17 @@ source "${LIB}/opt_hook_version.sh"
hook_color
hook_version
# -----------------------------------------------------------------------------
FORWARDED_ARGS=()
MAN_ARGS=()
BAT_ARGS=()
OPT_EXPORT_ENV=false
SHIFTOPT_SHORT_OPTIONS="SPLIT"
while shiftopt; do
case "$OPT" in
--paging|--pager) shiftval; BAT_ARGS+=("${OPT}=${OPT_VAL}") ;;
--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
@ -34,9 +40,76 @@ else
BAT_ARGS+=("--color=never" "--decorations=never")
fi
if [[ -z "${BAT_STYLE+x}" ]]; then
export BAT_STYLE="grid"
fi
# -----------------------------------------------------------------------------
export MANPAGER='sh -c "col -bx | '"$(printf "%q" "$EXECUTABLE_BAT")"' --language=man --style=grid '$(printf "%q " "${BAT_ARGS[@]}")'"'
# 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 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
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

@ -37,8 +37,9 @@
#
# -----------------------------------------------------------------------------
# shellcheck disable=SC1090 disable=SC2155
SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo ".")")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo ".")")/../lib" && pwd)"
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"
@ -66,13 +67,13 @@ if [[ "$#" -eq 0 ]]; then
# Detect the shell.
#
# This will directly check if the parent is fish, since there's a
# good chance that `bash` or `sh` will be invoking fish.
if [[ "$(basename -- "$(parent_executable | cut -f1 -d' ')")" == "fish" ]]; then
detected_shell="fish"
else
detected_shell="$(parent_shell)"
fi
# 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
@ -80,6 +81,12 @@ if [[ "$#" -eq 0 ]]; then
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"
@ -89,7 +96,7 @@ if [[ "$#" -eq 0 ]]; then
# 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:\n"
printc "\n%{DIM}# The following will enable colors when using batpipe with less:%{CLEAR}\n"
fi
# shellcheck disable=SC2016
@ -98,6 +105,10 @@ if [[ "$#" -eq 0 ]]; then
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"
@ -115,17 +126,47 @@ BATPIPE_INSIDE_LESS=false
BATPIPE_INSIDE_BAT=false
TERM_WIDTH="$(term_width)"
if [[ "$(basename -- "$(parent_executable "$(parent_executable_pid)" | cut -f1 -d' ')")" == less ]]; then
BATPIPE_INSIDE_LESS=true
elif [[ "$(basename -- "$(parent_executable | cut -f1 -d' ')")" == "$(basename -- "$EXECUTABLE_BAT")" ]]; then
BATPIPE_INSIDE_BAT=true
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:
# -----------------------------------------------------------------------------
BATPIPE_VIEWERS=("exa" "ls" "tar" "unzip" "gunzip" "xz")
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
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -135,6 +176,12 @@ viewer_exa_supports() {
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"
@ -145,6 +192,16 @@ viewer_exa_process() {
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 $?
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -174,17 +231,50 @@ viewer_tar_supports() {
viewer_tar_process() {
if [[ -n "$2" ]]; then
tar -xf "$1" -O "$2" | bat --file-name="$1/$2"
tar $3 -xf "$1" -O "$2" | bat --file-name="$1/$2"
else
batpipe_header "Viewing contents of archive: %{PATH}%s" "$1"
batpipe_subheader "To view files within the archive, add the file path after the archive."
tar -tvf "$1"
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
@ -199,8 +289,7 @@ viewer_unzip_process() {
if [[ -n "$2" ]]; then
unzip -p "$1" "$2" | bat --file-name="$1/$2"
else
batpipe_header "Viewing contents of archive: %{PATH}%s" "$1"
batpipe_subheader "To view files within the archive, add the file path after the archive."
batpipe_archive_header
unzip -l "$1"
return $?
fi
@ -264,6 +353,11 @@ batpipe_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.
#

View file

@ -284,9 +284,22 @@ else
fi
main() {
local last_rendered
local rendered
local term_width="$(term_width)"
BAT_ARGS+=("--terminal-width=$term_width")
while true; do
clear
"${FILES[@]}" 2>&1 | "$EXECUTABLE_BAT" "${BAT_ARGS[@]}"
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
}

View file

@ -14,6 +14,7 @@ source "${LIB}/str.sh"
source "${LIB}/print.sh"
source "${LIB}/version.sh"
source "${LIB}/check.sh"
source "${LIB}/term.sh"
# -----------------------------------------------------------------------------
# Init:
# -----------------------------------------------------------------------------
@ -22,7 +23,10 @@ hook_version
# Formatters:
# -----------------------------------------------------------------------------
FORMATTERS=("prettier" "rustfmt" "shfmt" "clangformat" "black")
FORMATTERS=(
"yq" "prettier" "rustfmt" "shfmt" "clangformat"
"black" "mix_format" "column"
)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -118,6 +122,96 @@ formatter_black_process() {
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:
# -----------------------------------------------------------------------------
@ -145,6 +239,9 @@ map_language_to_extension() {
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"
@ -272,6 +369,8 @@ OPT_LANGUAGE=
FILES=()
DEBUG_PRINT_FORMATTER=false
TERMINAL_WIDTH="$(term_width)"
# Parse arguments.
while shiftopt; do
case "$OPT" in
@ -289,7 +388,9 @@ while shiftopt; do
# bat options
-*) {
BAT_ARGS+=("$OPT=$OPT_VAL")
if [[ -n "$OPT_VAL" ]]; then BAT_ARGS+=("$OPT=$OPT_VAL");
else BAT_ARGS+=("$OPT");
fi
} ;;
# Files

View file

@ -1,14 +1,4 @@
#!/usr/bin/env bash
# Find the real fish.
HERE="$(cd "$(dirname "$0")" && pwd)"
while read -d ':' -r dir; do
if [[ "$dir" == "$HERE" || -z "$dir" ]]; then continue; fi
if [[ -f "${dir}/fish" ]]; then
TMPDIR= "${dir}/fish" "$@"
exit $?
fi
done <<<"$PATH:"
# Print error and exit.
echo "fish was not found on \$PATH" 1>&2
exit 127
# Run bash, but with executable name as `fish`.
exec -a "${SHIM_ARGV0:-fish}" bash "$@"
exit $?

View file

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

7
test/shimexec/nu Executable file
View file

@ -0,0 +1,7 @@
#!/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

@ -0,0 +1,107 @@
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

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

View file

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

View file

@ -0,0 +1,26 @@
───────┬────────────────────────────────
│ 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

@ -2,6 +2,8 @@ HAS_RIPGREP=false
setup() {
use_shim 'batgrep'
unset BAT_STYLE
if command -v rg &>/dev/null; then
HAS_RIPGREP=true
@ -14,6 +16,15 @@ 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
@ -102,3 +113,33 @@ test:search_from_stdin() {
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

@ -11,11 +11,35 @@ test:detected_bash_shell() {
}
test:detected_fish_shell() {
description "Test it can detect a bash shell."
command -v "fish" &>/dev/null || skip "Test requires fish shell."
output="$(SHELL="fish" fish --login -c "$(batpipe_path)")"
grep '^set -x' <<< "$output" >/dev/null || fail "Detected the wrong shell for fish."
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() {
@ -24,3 +48,14 @@ test:viewer_gzip() {
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

@ -10,6 +10,14 @@ use_pager() {
_detect_pager
}
use_no_pager() {
unset BAT_PAGER
unset PAGER
_configure_pager
_detect_pager
}
use_bat_pager() {
unset PAGER
export BAT_PAGER="$1"
@ -26,6 +34,14 @@ test:less_detection() {
(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[@]}"
}
test:less_version() {
description "Identify less version"
@ -82,6 +98,14 @@ test:env_bat_pager() {
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."

View file

@ -1 +1 @@
2021.04.06
2024.08.24