fold/scripts/format-code.sh

135 lines
3.1 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
MODE="fix"
GIT_ONLY=false
usage() {
cat <<'EOF'
Usage: scripts/format-code.sh [--check|--fix] [--git]
Modes:
--check Check formatting only
--fix Apply formatting changes
--git Limit scope to staged C++ files
Defaults to formatting all C++ files in src/, include/, and tests/.
Files under tests/fixtures/ are always excluded.
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--check)
MODE="check"
shift
;;
--fix)
MODE="fix"
shift
;;
--git)
GIT_ONLY=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
printf 'Unknown option: %s\n' "$1" >&2
usage >&2
exit 2
;;
esac
done
if [[ -n "${LLVM_BIN:-}" && -x "${LLVM_BIN}/clang-format" ]]; then
CLANG_FORMAT="${LLVM_BIN}/clang-format"
elif command -v clang-format >/dev/null 2>&1; then
CLANG_FORMAT="$(command -v clang-format)"
else
printf 'clang-format not found in PATH or LLVM_BIN\n' >&2
exit 1
fi
if [[ ! -f "$REPO_ROOT/.clang-format" ]]; then
printf '.clang-format not found at repo root\n' >&2
exit 1
fi
cd "$REPO_ROOT"
declare -a files=()
append_file_if_cpp() {
local file="$1"
[[ -f "$file" ]] || return 0
[[ "$file" == tests/fixtures/* ]] && return 0
case "$file" in
src/*|include/*|tests/*)
;;
*)
return 0
;;
esac
case "$file" in
*.c|*.cc|*.cpp|*.cxx|*.h|*.hh|*.hpp)
files+=("$file")
;;
esac
}
if [[ "$GIT_ONLY" == true ]]; then
while IFS= read -r file; do
[[ -n "$file" ]] || continue
append_file_if_cpp "$file"
done < <(git diff --cached --name-only --diff-filter=ACMR)
else
while IFS= read -r file; do
[[ -n "$file" ]] || continue
files+=("$file")
done < <(
find src include tests \( -type f \) \
\( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.cxx' -o -name '*.h' -o -name '*.hh' -o -name '*.hpp' \) \
-not -path 'tests/fixtures/*' \
| sort
)
fi
if [[ ${#files[@]} -eq 0 ]]; then
printf 'No C++ files to process\n'
exit 0
fi
if [[ "$GIT_ONLY" == true ]]; then
deduped=()
while IFS= read -r line; do
[[ -n "$line" ]] && deduped+=("$line")
done < <(printf '%s\n' "${files[@]}" | sort -u)
files=("${deduped[@]}")
fi
failures=0
for file in "${files[@]}"; do
if [[ "$MODE" == "check" ]]; then
if ! "$CLANG_FORMAT" --style=file --dry-run --Werror "$file"; then
failures=$((failures + 1))
fi
else
"$CLANG_FORMAT" --style=file -i "$file"
fi
done
if [[ "$MODE" == "check" ]]; then
if [[ "$failures" -gt 0 ]]; then
printf '%d file(s) need formatting\n' "$failures" >&2
exit 1
fi
printf 'Formatting check passed\n'
exit 0
fi
printf 'Formatted %d file(s)\n' "${#files[@]}"