| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  | #!/bin/bash
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | onoe() { | 
					
						
							|  |  |  |   echo "$*" >&2 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | odie() { | 
					
						
							|  |  |  |   onoe "$@" | 
					
						
							|  |  |  |   exit 1 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  | # HOMEBREW_PREFIX is set by extend/ENV/super.rb | 
					
						
							|  |  |  | # shellcheck disable=SC2154 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | if [[ -z "${HOMEBREW_PREFIX}" ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  | then | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  |   odie "${0##*/}: This program is internal and must be run via brew." | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  | fi | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # HOMEBREW_PREFIX is set by extend/ENV/super.rb | 
					
						
							|  |  |  | # shellcheck disable=SC2154 | 
					
						
							|  |  |  | SHFMT="${HOMEBREW_PREFIX}/opt/shfmt/bin/shfmt" | 
					
						
							|  |  |  | if [[ ! -x "${SHFMT}" ]] | 
					
						
							|  |  |  | then | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  |   odie "${0##*/}: Please install shfmt by running \`brew install shfmt\`." | 
					
						
							|  |  |  | fi | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-27 21:13:11 +08:00
										 |  |  | # HOMEBREW_PREFIX is set by extend/ENV/super.rb | 
					
						
							|  |  |  | # shellcheck disable=SC2154 | 
					
						
							|  |  |  | DIFF="${HOMEBREW_PREFIX}/opt/diffutils/bin/diff" | 
					
						
							|  |  |  | DIFF_ARGS=("-d" "-C" "1") | 
					
						
							|  |  |  | if [[ ! -x "${DIFF}" ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | then | 
					
						
							| 
									
										
										
										
											2021-09-27 21:13:11 +08:00
										 |  |  |   # HOMEBREW_PATH is set by global.rb | 
					
						
							|  |  |  |   # shellcheck disable=SC2154 | 
					
						
							|  |  |  |   if [[ -x "$(PATH="${HOMEBREW_PATH}" command -v diff)" ]] | 
					
						
							|  |  |  |   then | 
					
						
							|  |  |  |     DIFF="$(PATH="${HOMEBREW_PATH}" command -v diff)" # fall back to `diff` in PATH without coloring | 
					
						
							| 
									
										
										
										
											2021-10-12 02:12:48 +08:00
										 |  |  |   elif [[ -z "${HOMEBREW_PATH}" && -x "$(command -v diff)" ]] | 
					
						
							|  |  |  |   then | 
					
						
							|  |  |  |     # HOMEBREW_PATH may unset if shfmt.sh is called by vscode | 
					
						
							|  |  |  |     DIFF="$(command -v diff)" # fall back to `diff` in PATH without coloring | 
					
						
							| 
									
										
										
										
											2021-09-27 21:13:11 +08:00
										 |  |  |   else | 
					
						
							|  |  |  |     odie "${0##*/}: Please install diff by running \`brew install diffutils\`." | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  | else | 
					
						
							|  |  |  |   DIFF_ARGS+=("--color") # enable color output for GNU diff | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  | fi | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SHFMT_ARGS=() | 
					
						
							|  |  |  | INPLACE='' | 
					
						
							|  |  |  | while [[ $# -gt 0 ]] | 
					
						
							|  |  |  | do | 
					
						
							|  |  |  |   arg="$1" | 
					
						
							|  |  |  |   if [[ "${arg}" == "--" ]] | 
					
						
							|  |  |  |   then | 
					
						
							|  |  |  |     shift | 
					
						
							|  |  |  |     break | 
					
						
							|  |  |  |   fi | 
					
						
							| 
									
										
										
										
											2022-06-18 17:27:56 +08:00
										 |  |  |   if [[ "${arg}" == "-w" || "${arg}" == "--write" ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  |   then | 
					
						
							|  |  |  |     shift | 
					
						
							|  |  |  |     INPLACE=1 | 
					
						
							|  |  |  |     continue | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  |   SHFMT_ARGS+=("${arg}") | 
					
						
							|  |  |  |   shift | 
					
						
							|  |  |  | done | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  | unset arg | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | FILES=() | 
					
						
							|  |  |  | for file in "$@" | 
					
						
							|  |  |  | do | 
					
						
							|  |  |  |   if [[ -f "${file}" ]] | 
					
						
							|  |  |  |   then | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     if [[ -w "${file}" ]] | 
					
						
							|  |  |  |     then | 
					
						
							|  |  |  |       FILES+=("${file}") | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       onoe "${0##*/}: File \"${file}\" is not writable." | 
					
						
							|  |  |  |     fi | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  |   else | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     onoe "${0##*/}: File \"${file}\" does not exist." | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  |     exit 1 | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  | done | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  | unset file | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 02:12:48 +08:00
										 |  |  | STDIN='' | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  | if [[ "${#FILES[@]}" == 0 ]] | 
					
						
							|  |  |  | then | 
					
						
							| 
									
										
										
										
											2021-10-12 02:12:48 +08:00
										 |  |  |   FILES=(/dev/stdin) | 
					
						
							|  |  |  |   STDIN=1 | 
					
						
							| 
									
										
										
										
											2021-09-15 14:58:09 +08:00
										 |  |  | fi | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | ### | 
					
						
							|  |  |  | ### Custom shell script styling | 
					
						
							|  |  |  | ### | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  | # Check for specific patterns and prompt messages if detected | 
					
						
							| 
									
										
										
										
											2023-07-17 12:30:12 -07:00
										 |  |  | no_forbidden_pattern() { | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  |   local file="$1" | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |   local tempfile="$2" | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |   local subject="$3" | 
					
						
							|  |  |  |   local message="$4" | 
					
						
							|  |  |  |   local regex_pos="$5" | 
					
						
							|  |  |  |   local regex_neg="${6:-}" | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |   local line | 
					
						
							|  |  |  |   local num=0 | 
					
						
							|  |  |  |   local retcode=0 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |   while IFS='' read -r line | 
					
						
							|  |  |  |   do | 
					
						
							|  |  |  |     num="$((num + 1))" | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |     if [[ "${line}" =~ ${regex_pos} ]] && | 
					
						
							|  |  |  |        [[ -z "${regex_neg}" || ! "${line}" =~ ${regex_neg} ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |     then | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |       onoe "${subject} detected at \"${file}\", line ${num}." | 
					
						
							|  |  |  |       [[ -n "${message}" ]] && onoe "${message}" | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |       retcode=1 | 
					
						
							|  |  |  |     fi | 
					
						
							|  |  |  |   done <"${file}" | 
					
						
							|  |  |  |   return "${retcode}" | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  | # Check pattern: | 
					
						
							|  |  |  | # '^\t+' | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Replace tabs with 2 spaces instead | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | no_tabs() { | 
					
						
							|  |  |  |   local file="$1" | 
					
						
							|  |  |  |   local tempfile="$2" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-17 12:30:12 -07:00
										 |  |  |   no_forbidden_pattern "${file}" "${tempfile}" \
 | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |     "Indent with tab" \
 | 
					
						
							|  |  |  |     'Replace tabs with 2 spaces instead.' \
 | 
					
						
							|  |  |  |     '^[[:space:]]+' \
 | 
					
						
							|  |  |  |     '^ +' | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | # Check pattern: | 
					
						
							|  |  |  | # for var in ... \ | 
					
						
							|  |  |  | #            ...; do | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Use the followings instead (keep for statements only one line): | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | #   ARRAY=( | 
					
						
							|  |  |  | #     ... | 
					
						
							|  |  |  | #   ) | 
					
						
							|  |  |  | #   for var in "${ARRAY[@]}" | 
					
						
							|  |  |  | #   do | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | # | 
					
						
							|  |  |  | no_multiline_for_statements() { | 
					
						
							|  |  |  |   local file="$1" | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |   local tempfile="$2" | 
					
						
							|  |  |  |   local regex='^ *for [_[:alnum:]]+ in .*\\$' | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |   local message | 
					
						
							|  |  |  |   message="$( | 
					
						
							|  |  |  |     cat <<EOMSG | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | Use the followings instead (keep for statements only one line): | 
					
						
							|  |  |  |   ARRAY=( | 
					
						
							|  |  |  |     ... | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  |   for var in "\${ARRAY[@]}" | 
					
						
							|  |  |  |   do | 
					
						
							|  |  |  |     ... | 
					
						
							|  |  |  |   done | 
					
						
							|  |  |  | EOMSG | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |   )"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-17 12:30:12 -07:00
										 |  |  |   no_forbidden_pattern "${file}" "${tempfile}" \
 | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |     "Multiline for statement" \
 | 
					
						
							|  |  |  |     "${message}" \
 | 
					
						
							|  |  |  |     "${regex}" | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Check pattern: | 
					
						
							|  |  |  | # IFS=$'\n' | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Use the followings instead: | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | #   while IFS='' read -r line | 
					
						
							|  |  |  | #   do | 
					
						
							|  |  |  | #     ... | 
					
						
							|  |  |  | #   done < <(command) | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | # | 
					
						
							|  |  |  | no_IFS_newline() { | 
					
						
							|  |  |  |   local file="$1" | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |   local tempfile="$2" | 
					
						
							|  |  |  |   local regex="^[^#]*IFS=\\\$'\\\\n'" | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |   local message | 
					
						
							|  |  |  |   message="$( | 
					
						
							|  |  |  |     cat <<EOMSG | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | Use the followings instead: | 
					
						
							|  |  |  |   while IFS='' read -r line | 
					
						
							|  |  |  |   do | 
					
						
							|  |  |  |     ... | 
					
						
							|  |  |  |   done < <(command) | 
					
						
							|  |  |  | EOMSG | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |   )"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-17 12:30:12 -07:00
										 |  |  |   no_forbidden_pattern "${file}" "${tempfile}" \
 | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |     "Pattern \`IFS=\$'\\n'\`" \
 | 
					
						
							|  |  |  |     "${message}" \
 | 
					
						
							|  |  |  |     "${regex}" | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Combine all forbidden styles | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  | no_forbidden_styles() { | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |   local file="$1" | 
					
						
							|  |  |  |   local tempfile="$2" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   no_tabs "${file}" "${tempfile}" || return 1 | 
					
						
							|  |  |  |   no_multiline_for_statements "${file}" "${tempfile}" || return 1 | 
					
						
							|  |  |  |   no_IFS_newline "${file}" "${tempfile}" || return 1 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | # Align multiline if condition (indent with 3 spaces or 6 spaces (start with "-")) | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | # before:                   after: | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | #   if [[ ... ]] ||           if [[ ... ]] || | 
					
						
							|  |  |  | #     [[ ... ]]                  [[ ... ]] | 
					
						
							|  |  |  | #   then                      then | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | # | 
					
						
							|  |  |  | # before:                   after: | 
					
						
							| 
									
										
										
										
											2021-09-16 23:26:23 +08:00
										 |  |  | #   if [[ -n ... ||           if [[ -n ... || | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | #     -n ... ]]                     -n ... ]] | 
					
						
							|  |  |  | #   then                      then | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | align_multiline_if_condition() { | 
					
						
							|  |  |  |   local line | 
					
						
							|  |  |  |   local lastline='' | 
					
						
							| 
									
										
										
										
											2021-11-12 19:22:40 +08:00
										 |  |  |   local base_indent=''       # indents before `if` | 
					
						
							|  |  |  |   local elif_extra_indent='' # 2 extra spaces for `elif` | 
					
						
							| 
									
										
										
										
											2021-11-12 19:07:55 +08:00
										 |  |  |   local multiline_if_then_begin_regex='^( *)(el)?if ' | 
					
						
							|  |  |  |   local multiline_if_then_end_regex='^(.*)\; (then( *#.*)?)$' | 
					
						
							| 
									
										
										
										
											2021-11-12 19:22:40 +08:00
										 |  |  |   local within_test_regex='^( *)(((! )?-[fdLrwxeszn] )|([^\[]+ (==|!=|=~) ))' | 
					
						
							| 
									
										
										
										
											2021-09-16 23:26:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   trim() { | 
					
						
							|  |  |  |     [[ "$1" =~ [^[:space:]](.*[^[:space:]])? ]] | 
					
						
							|  |  |  |     printf "%s" "${BASH_REMATCH[0]}" | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 19:07:55 +08:00
										 |  |  |   if [[ "$1" =~ ${multiline_if_then_begin_regex} ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |   then | 
					
						
							|  |  |  |     base_indent="${BASH_REMATCH[1]}" | 
					
						
							| 
									
										
										
										
											2021-11-12 19:22:40 +08:00
										 |  |  |     [[ -n "${BASH_REMATCH[2]}" ]] && elif_extra_indent='  ' # 2 extra spaces for `elif` | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     echo "$1" | 
					
						
							|  |  |  |     shift | 
					
						
							|  |  |  |   fi | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |   while [[ "$#" -gt 0 ]] | 
					
						
							|  |  |  |   do | 
					
						
							|  |  |  |     line="$1" | 
					
						
							|  |  |  |     shift | 
					
						
							| 
									
										
										
										
											2021-11-12 19:07:55 +08:00
										 |  |  |     if [[ "${line}" =~ ${multiline_if_then_end_regex} ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     then | 
					
						
							|  |  |  |       line="${BASH_REMATCH[1]}" | 
					
						
							|  |  |  |       lastline="${base_indent}${BASH_REMATCH[2]}" | 
					
						
							|  |  |  |     fi | 
					
						
							|  |  |  |     if [[ "${line}" =~ ${within_test_regex} ]] | 
					
						
							|  |  |  |     then | 
					
						
							| 
									
										
										
										
											2021-11-12 19:22:40 +08:00
										 |  |  |       # Add 3 extra spaces (6 spaces in total) to multiline test conditions | 
					
						
							|  |  |  |       # before:                   after: | 
					
						
							|  |  |  |       #   if [[ -n ... ||           if [[ -n ... || | 
					
						
							|  |  |  |       #     -n ... ]]                     -n ... ]] | 
					
						
							|  |  |  |       #   then                      then | 
					
						
							|  |  |  |       echo "${base_indent}${elif_extra_indent}      $(trim "${line}")" | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     else | 
					
						
							| 
									
										
										
										
											2021-11-12 19:22:40 +08:00
										 |  |  |       echo "${base_indent}${elif_extra_indent}   $(trim "${line}")" | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     fi | 
					
						
							|  |  |  |   done | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   echo "${lastline}" | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | # Wrap `then` and `do` to a separated line | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | # before:                   after: | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | #   if [[ ... ]]; then        if [[ ... ]] | 
					
						
							|  |  |  | #                             then | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | # | 
					
						
							|  |  |  | # before:                   after: | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | #   if [[ ... ]] ||           if [[ ... ]] || | 
					
						
							| 
									
										
										
										
											2021-09-16 23:26:23 +08:00
										 |  |  | #     [[ ... ]]; then            [[ ... ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | #                             then | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | # | 
					
						
							|  |  |  | # before:                   after: | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  | #   for var in ...; do        for var in ... | 
					
						
							|  |  |  | #                             do | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | wrap_then_do() { | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  |   local file="$1" | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |   local tempfile="$2" | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-16 23:26:23 +08:00
										 |  |  |   local -a processed=() | 
					
						
							|  |  |  |   local -a buffer=() | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |   local line | 
					
						
							| 
									
										
										
										
											2021-11-12 19:23:10 +08:00
										 |  |  |   local singleline_if_then_fi_regex='^( *)if (.+)\; then (.+)\; fi( *#.*)?$' | 
					
						
							| 
									
										
										
										
											2021-11-12 19:07:55 +08:00
										 |  |  |   local singleline_if_then_regex='^( *)(el)?if (.+)\; (then( *#.*)?)$' | 
					
						
							|  |  |  |   local singleline_for_do_regex='^( *)(for|while) (.+)\; (do( *#.*)?)$' | 
					
						
							|  |  |  |   local multiline_if_then_begin_regex='^( *)(el)?if ' | 
					
						
							|  |  |  |   local multiline_if_then_end_regex='^(.*)\; (then( *#.*)?)$' | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   while IFS='' read -r line | 
					
						
							|  |  |  |   do | 
					
						
							|  |  |  |     if [[ "${#buffer[@]}" == 0 ]] | 
					
						
							|  |  |  |     then | 
					
						
							| 
									
										
										
										
											2021-11-12 19:23:10 +08:00
										 |  |  |       if [[ "${line}" =~ ${singleline_if_then_fi_regex} ]] | 
					
						
							|  |  |  |       then | 
					
						
							|  |  |  |         processed+=("${line}") | 
					
						
							|  |  |  |       elif [[ "${line}" =~ ${singleline_if_then_regex} ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |       then | 
					
						
							|  |  |  |         processed+=("${BASH_REMATCH[1]}${BASH_REMATCH[2]}if ${BASH_REMATCH[3]}") | 
					
						
							|  |  |  |         processed+=("${BASH_REMATCH[1]}${BASH_REMATCH[4]}") | 
					
						
							| 
									
										
										
										
											2021-11-12 19:07:55 +08:00
										 |  |  |       elif [[ "${line}" =~ ${singleline_for_do_regex} ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |       then | 
					
						
							|  |  |  |         processed+=("${BASH_REMATCH[1]}${BASH_REMATCH[2]} ${BASH_REMATCH[3]}") | 
					
						
							|  |  |  |         processed+=("${BASH_REMATCH[1]}${BASH_REMATCH[4]}") | 
					
						
							| 
									
										
										
										
											2021-11-12 19:07:55 +08:00
										 |  |  |       elif [[ "${line}" =~ ${multiline_if_then_begin_regex} ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |       then | 
					
						
							|  |  |  |         buffer=("${line}") | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         processed+=("${line}") | 
					
						
							|  |  |  |       fi | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       buffer+=("${line}") | 
					
						
							| 
									
										
										
										
											2021-11-12 19:07:55 +08:00
										 |  |  |       if [[ "${line}" =~ ${multiline_if_then_end_regex} ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |       then | 
					
						
							|  |  |  |         while IFS='' read -r line | 
					
						
							|  |  |  |         do | 
					
						
							|  |  |  |           processed+=("${line}") | 
					
						
							|  |  |  |         done < <(align_multiline_if_condition "${buffer[@]}") | 
					
						
							|  |  |  |         buffer=() | 
					
						
							|  |  |  |       fi | 
					
						
							|  |  |  |     fi | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |   done <"${tempfile}" | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |   printf "%s\n" "${processed[@]}" >"${tempfile}" | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-16 23:26:23 +08:00
										 |  |  | # TODO: It's hard to align multiline switch cases | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | align_multiline_switch_cases() { | 
					
						
							|  |  |  |   true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:42:25 +08:00
										 |  |  | # Return codes: | 
					
						
							|  |  |  | #   0: success, good styles | 
					
						
							|  |  |  | #   1: file system permission errors | 
					
						
							|  |  |  | #   2: shfmt failed | 
					
						
							|  |  |  | #   3: forbidden styles detected | 
					
						
							|  |  |  | #   4: bad styles but can be auto-fixed | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | format() { | 
					
						
							|  |  |  |   local file="$1" | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |   local tempfile | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 02:12:48 +08:00
										 |  |  |   if [[ -n "${STDIN}" ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |   then | 
					
						
							| 
									
										
										
										
											2021-10-12 02:12:48 +08:00
										 |  |  |     tempfile="$(mktemp)" | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     if [[ ! -f "${file}" || ! -r "${file}" ]] | 
					
						
							|  |  |  |     then | 
					
						
							|  |  |  |       onoe "File \"${file}\" is not readable." | 
					
						
							|  |  |  |       return 1 | 
					
						
							|  |  |  |     fi | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 19:07:55 +08:00
										 |  |  |     tempfile="$(dirname "${file}")/.${file##*/}.formatted~" | 
					
						
							| 
									
										
										
										
											2021-10-12 02:12:48 +08:00
										 |  |  |     cp -af "${file}" "${tempfile}" | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |   fi | 
					
						
							| 
									
										
										
										
											2021-10-12 02:12:48 +08:00
										 |  |  |   trap 'rm -f "${tempfile}" 2>/dev/null' RETURN | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  |   # Format with `shfmt` first | 
					
						
							| 
									
										
										
										
											2021-10-12 02:12:48 +08:00
										 |  |  |   if [[ -z "${STDIN}" ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |   then | 
					
						
							| 
									
										
										
										
											2021-10-12 02:12:48 +08:00
										 |  |  |     if [[ ! -f "${tempfile}" || ! -w "${tempfile}" ]] | 
					
						
							|  |  |  |     then | 
					
						
							|  |  |  |       onoe "File \"${tempfile}\" is not writable." | 
					
						
							|  |  |  |       return 1 | 
					
						
							|  |  |  |     fi | 
					
						
							|  |  |  |     if ! "${SHFMT}" -w "${SHFMT_ARGS[@]}" "${tempfile}" | 
					
						
							|  |  |  |     then | 
					
						
							|  |  |  |       onoe "Failed to run \`shfmt\` for file \"${file}\"." | 
					
						
							|  |  |  |       return 2 | 
					
						
							|  |  |  |     fi | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     if ! "${SHFMT}" "${SHFMT_ARGS[@]}" >"${tempfile}" | 
					
						
							|  |  |  |     then | 
					
						
							|  |  |  |       onoe "Failed to run \`shfmt\` for file \"${file}\"." | 
					
						
							|  |  |  |       return 2 | 
					
						
							|  |  |  |     fi | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |   fi | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-16 23:16:07 +08:00
										 |  |  |   # Fail fast when forbidden styles detected | 
					
						
							| 
									
										
										
										
											2021-10-07 19:42:25 +08:00
										 |  |  |   no_forbidden_styles "${file}" "${tempfile}" || return 3 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   # Tweak it with custom shell script styles | 
					
						
							| 
									
										
										
										
											2021-09-15 23:40:14 +08:00
										 |  |  |   wrap_then_do "${file}" "${tempfile}" | 
					
						
							|  |  |  |   align_multiline_switch_cases "${file}" "${tempfile}" | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 02:12:48 +08:00
										 |  |  |   if [[ -n "${STDIN}" ]] | 
					
						
							|  |  |  |   then | 
					
						
							|  |  |  |     cat "${tempfile}" | 
					
						
							|  |  |  |     return 0 | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-27 21:13:11 +08:00
										 |  |  |   if ! "${DIFF}" -q "${file}" "${tempfile}" &>/dev/null | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  |   then | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     if [[ -n "${INPLACE}" ]] | 
					
						
							|  |  |  |     then | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  |       cp -af "${tempfile}" "${file}" | 
					
						
							| 
									
										
										
										
											2021-10-07 19:42:25 +08:00
										 |  |  |     else | 
					
						
							| 
									
										
										
										
											2021-11-12 19:07:55 +08:00
										 |  |  |       # Show a linebreak between outputs | 
					
						
							|  |  |  |       [[ "${RETCODE}" != 0 ]] && onoe | 
					
						
							| 
									
										
										
										
											2021-10-07 19:42:25 +08:00
										 |  |  |       # Show differences | 
					
						
							| 
									
										
										
										
											2021-10-07 19:48:53 +08:00
										 |  |  |       "${DIFF}" "${DIFF_ARGS[@]}" "${file}" "${tempfile}" 1>&2 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  |     fi | 
					
						
							| 
									
										
										
										
											2021-10-07 19:42:25 +08:00
										 |  |  |     return 4 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  |   else | 
					
						
							|  |  |  |     # File is identical between code formations (good styling) | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     return 0 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  |   fi | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | RETCODE=0 | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | for file in "${FILES[@]}" | 
					
						
							|  |  |  | do | 
					
						
							| 
									
										
										
										
											2021-10-07 19:42:25 +08:00
										 |  |  |   retcode='' | 
					
						
							|  |  |  |   if [[ -n "${INPLACE}" ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |   then | 
					
						
							| 
									
										
										
										
											2021-10-07 19:42:25 +08:00
										 |  |  |     INPLACE=1 format "${file}" | 
					
						
							|  |  |  |     retcode="$?" | 
					
						
							|  |  |  |     if [[ "${retcode}" == 4 ]] | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     then | 
					
						
							| 
									
										
										
										
											2021-10-07 22:16:57 +08:00
										 |  |  |       onoe "${0##*/}: Bad styles detected in file \"${file}\", fixing..." | 
					
						
							| 
									
										
										
										
											2021-10-07 19:42:25 +08:00
										 |  |  |       retcode='' | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     fi | 
					
						
							| 
									
										
										
										
											2021-10-07 19:42:25 +08:00
										 |  |  |   fi | 
					
						
							|  |  |  |   if [[ -z "${retcode}" ]] | 
					
						
							|  |  |  |   then | 
					
						
							|  |  |  |     INPLACE='' format "${file}" | 
					
						
							|  |  |  |     retcode="$?" | 
					
						
							|  |  |  |   fi | 
					
						
							|  |  |  |   if [[ "${retcode}" != 0 ]] | 
					
						
							|  |  |  |   then | 
					
						
							|  |  |  |     case "${retcode}" in | 
					
						
							|  |  |  |       1) onoe "${0##*/}: Failed to format file \"${file}\". Formatter exited with code 1 (permission error)." ;; | 
					
						
							|  |  |  |       2) onoe "${0##*/}: Failed to format file \"${file}\". Formatter exited with code 2 (\`shfmt\` failed)." ;; | 
					
						
							|  |  |  |       3) onoe "${0##*/}: Failed to format file \"${file}\". Formatter exited with code 3 (forbidden styles detected)." ;; | 
					
						
							|  |  |  |       4) onoe "${0##*/}: Fixable bad styles detected in file \"${file}\", run \`brew style --fix\` to apply. Formatter exited with code 4." ;; | 
					
						
							|  |  |  |       *) onoe "${0##*/}: Failed to format file \"${file}\". Formatter exited with code ${retcode}." ;; | 
					
						
							|  |  |  |     esac | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  |     RETCODE=1 | 
					
						
							|  |  |  |   fi | 
					
						
							| 
									
										
										
										
											2021-09-15 17:00:50 +08:00
										 |  |  | done | 
					
						
							| 
									
										
										
										
											2021-09-15 22:12:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | exit "${RETCODE}" |