#!/bin/bash onoe() { echo "$*" >&2 } odie() { onoe "$@" exit 1 } # HOMEBREW_PREFIX is set by extend/ENV/super.rb # shellcheck disable=SC2154 if [[ -z "${HOMEBREW_PREFIX}" ]] then odie "${0##*/}: This program is internal and must be run via brew." fi # HOMEBREW_PREFIX is set by extend/ENV/super.rb # shellcheck disable=SC2154 SHFMT="${HOMEBREW_PREFIX}/opt/shfmt/bin/shfmt" if [[ ! -x "${SHFMT}" ]] then odie "${0##*/}: Please install shfmt by running \`brew install shfmt\`." fi if [[ ! -x "$(command -v diff)" ]] then odie "${0##*/}: Please install diff by running \`brew install diffutils\`." fi SHFMT_ARGS=() INPLACE='' while [[ $# -gt 0 ]] do arg="$1" if [[ "${arg}" == "--" ]] then shift break fi if [[ "${arg}" == "-w" ]] then shift INPLACE=1 continue fi SHFMT_ARGS+=("${arg}") shift done unset arg FILES=() for file in "$@" do if [[ -f "${file}" ]] then if [[ -w "${file}" ]] then FILES+=("${file}") else onoe "${0##*/}: File \"${file}\" is not writable." fi else onoe "${0##*/}: File \"${file}\" does not exist." exit 1 fi done unset file if [[ "${#FILES[@]}" == 0 ]] then exit fi ### ### Custom shell script styling ### # Check for specific patterns and prompt messages if detected no_forbidden_patten() { local file="$1" local tempfile="$2" local subject="$3" local message="$4" local regex_pos="$5" local regex_neg="${6:-}" local line local num=0 local retcode=0 while IFS='' read -r line do num="$((num + 1))" if [[ "${line}" =~ ${regex_pos} ]] && [[ -z "${regex_neg}" || ! "${line}" =~ ${regex_neg} ]] then onoe "${subject} detected at \"${file}\", line ${num}." [[ -n "${message}" ]] && onoe "${message}" retcode=1 fi done <"${file}" return "${retcode}" } # Check pattern: # '^\t+' # # Replace tabs with 2 spaces instead # no_tabs() { local file="$1" local tempfile="$2" no_forbidden_patten "${file}" "${tempfile}" \ "Indent with tab" \ 'Replace tabs with 2 spaces instead.' \ '^[[:space:]]+' \ '^ +' } # Check pattern: # for var in ... \ # ...; do # # Use the followings instead (keep for statements only one line): # ARRAY=( # ... # ) # for var in "${ARRAY[@]}" # do # no_multiline_for_statements() { local file="$1" local tempfile="$2" local regex='^ *for [_[:alnum:]]+ in .*\\$' local message message="$( cat <"${tempfile}" } # TODO: It's hard to align multiline switch cases align_multiline_switch_cases() { true } format() { local file="$1" local tempfile if [[ ! -f "${file}" || ! -r "${file}" ]] then onoe "File \"${file}\" is not readable." return 1 fi tempfile="$(dirname "${file}")/.${file##*/}.temp" trap 'rm -f "${tempfile}" 2>/dev/null' RETURN cp -af "${file}" "${tempfile}" if [[ ! -f "${tempfile}" || ! -w "${tempfile}" ]] then onoe "File \"${tempfile}\" is not writable." return 1 fi # Format with `shfmt` first if ! "${SHFMT}" -w "${SHFMT_ARGS[@]}" "${tempfile}" then onoe "Failed to run \`shfmt\` for file \"${file}\"." return 1 fi # Fail fast when forbidden styles detected ! no_forbidden_styles "${file}" "${tempfile}" && return 2 # Tweak it with custom shell script styles wrap_then_do "${file}" "${tempfile}" align_multiline_switch_cases "${file}" "${tempfile}" if ! diff -q "${file}" "${tempfile}" &>/dev/null then # Show differences diff -d -C 1 --color=auto "${file}" "${tempfile}" if [[ -n "${INPLACE}" ]] then cp -af "${tempfile}" "${file}" fi return 2 else # File is identical between code formations (good styling) return 0 fi } RETCODE=0 for file in "${FILES[@]}" do format "${file}" retcode="$?" if [[ "${retcode}" != 0 ]] then if [[ "${retcode}" == 1 ]] then onoe "${0##*/}: Failed to format file \"${file}\". Formatter exited with code 1." else onoe "${0##*/}: Bad style for file \"${file}\". Formatter exited with code 2." fi onoe RETCODE=1 fi done exit "${RETCODE}"