Permalink
Fetching contributors…
Cannot retrieve contributors at this time
277 lines (233 sloc) 5.84 KB
#! ~/.bash/functions-diff
# =========================================================================
# http://stackoverflow.com/q/8800578/100073
# https://retracile.net/blog/2013/06/01/22.00
# http://www.colordiff.org/
diff() {
# Colors
local diff_c_reset='\x1b[0m'
local diff_b_meta="40;1;37" # white
local diff_b_frag="36" # cyan
local diff_b_old="31" # red
local diff_b_new="32" # green
local diff_b_mod="34" # blue
[ $# -gt 0 ] || {
cat - <<HELP
Usage: $FUNCNAME [OPTION]... FILES
Wrapper for diff to colorize output of diff for better readability.
Try "diff --help" for more information.
OPTIONS
Diff options
All options will be passed to "diff".
Coloring options
--color[=WHEN], --colour[=WHEN]
Controls the colorizing method. WHEN is "always" (the default value if not
specified explicitly), "never" or "auto". To make affect globally, set one
of these values to CDIFF_WHEN environment variable.
ENVIRONMENT VARIABLES
CDIFF_COLORS
This environment variable is used to specify colors to highlight the
separate parts of the diff output. Its value is a colon-separated list of
capabilities. Names and responsibility of each capability correspond to
the configuration parameters "color.diff.<slot>" used in "git config".
Values are integers in decimal representation and can be concatenated with
semicolons. Further these values are assembled into ANSI escape codes.
meta=$diff_b_meta
Metainformation (names of compared files)
frag=$diff_b_frag
Hunk header (line numbers of changed lines)
old=$diff_b_old
Removed lines
new=$diff_b_new
Added lines
mod=$diff_b_mod
Modified lines
CDIFF_WHEN
Colorizing method does effect on all runs; assumes the same values as for
the "--color" option. The default value is "auto" and can be superseded by
the "--color" option.
HELP
return
}
# Blocks
local CDIFF_COLORS="${CDIFF_COLORS:-meta=$diff_b_meta:frag=$diff_b_frag:old=$diff_b_old:new=$diff_b_new:mod=$diff_b_mod}"
local c
for c in ${CDIFF_COLORS//:/ }
do
[ -n "${c#*=}" ] || break
eval "diff_b_${c%%=*}='${c#*=}'"
done
diff_b_meta="\\x1b[${diff_b_meta}m"
diff_b_frag="\\x1b[${diff_b_frag}m"
diff_b_old="\\x1b[${diff_b_old}m"
diff_b_new="\\x1b[${diff_b_new}m"
diff_b_mod="\\x1b[${diff_b_mod}m"
# Schemes
local diff_s_normal="
# diff ...
# File headers
/^[A-Za-z]/ s/^/$diff_b_meta/;
# Difference headers
/^[0-9]/ s/^/$diff_b_frag/;
# Changed lines
/^< / s/^/$diff_b_old/;
/^> / s/^/$diff_b_new/;
"
local diff_s_context="
# diff -c ...
# File headers
/^[A-Za-z0-9]/ s/^/$diff_b_meta/;
/^\**$/ s/^/$diff_b_meta/;
# File or difference headers
/^\*\*\* / {
/\*\*\*\*$/! s/^/$diff_b_meta/;
/\*\*\*\*$/ s/^/$diff_b_frag/;
};
/^--- / {
/----$/! s/^/$diff_b_meta/;
/----$/ s/^/$diff_b_frag/;
};
# Changed lines
/^! / s/^/$diff_b_mod/;
/^- / s/^/$diff_b_old/;
/^+ / s/^/$diff_b_new/;
"
local diff_s_unified="
# diff -u ...
# File headers
/^[A-Za-z]/ s/^/$diff_b_meta/;
/^--- / s/^/$diff_b_meta/;
/^+++ / s/^/$diff_b_meta/;
# Difference headers
/^@@ [^@]* [^@]* @@/ s/^/$diff_b_frag/;
# Changed lines
/^-/ s/^/$diff_b_old/;
/^+/ s/^/$diff_b_new/;
"
local diff_s_ed="
# diff -e ...
# File headers
/^diff \(-e\|--ed\) / s/^/$diff_b_meta/;
/^Common subdirectories: / s/^/$diff_b_meta/;
/^Only in / s/^/$diff_b_meta/;
/^Files .* differ\$/ s/^/$diff_b_meta/;
# Difference headers
/^\([0-9][0-9]*,\)\?[0-9][0-9]*[acd]\$/ {
/a\$/ s/^/$diff_b_new/;
/c\$/ s/^/$diff_b_mod/;
/d\$/ s/^/$diff_b_old/;
}
"
local diff_s_rcs="
# diff -n ...
# File headers
/^diff \(-n\|--rcs\) / s/^/$diff_b_meta/;
/^Common subdirectories: / s/^/$diff_b_meta/;
/^Only in / s/^/$diff_b_meta/;
/^Files .* differ\$/ s/^/$diff_b_meta/;
# Difference headers
/^[ad][0-9][0-9]* [0-9][0-9]*\$/ {
/^d/ s/^/$diff_b_old/;
/^a/ s/^/$diff_b_new/;
}
"
local diff_s_sidebyside="
# diff -y ...
# File headers
/^diff \(-y\|--side-by-side\) / s/^/$diff_b_meta/;
/^Common subdirectories: / s/^/$diff_b_meta/;
/^Only in / s/^/$diff_b_meta/;
/^Files .* differ\$/ s/^/$diff_b_meta/;
# Changed lines
/^.* <\$/ s/^/$diff_b_old/;
/^.*[\t ]*|\t.*$/ s/^/$diff_b_mod/;
/^[\t ]*>$/ s/^/$diff_b_new/;
/^[\t ]*>\t.*/ s/^/$diff_b_new/;
"
local diff_scheme="$diff_s_normal"
# Assume "auto" if not specified
local CDIFF_WHEN="${CDIFF_WHEN:-auto}"
# Validate environment variable
case "$CDIFF_WHEN" in
always | never | auto )
# Do nothing: it is valid value
;;
* )
echo "Invalid value '$CDIFF_WHEN' in CDIFF_WHEN" >&2
return 2
;;
esac
local -a args
while [ $# -gt 0 ]
do
case "$1" in
--help | -v | --version )
diff_scheme=""
break
;;
-- )
break
;;
--color | --colour )
CDIFF_WHEN=always
;;
--color=* | --colour=* )
CDIFF_WHEN="${1#*=}"
;;
* )
case "$1" in
-c | -C* | --context | --context=* )
diff_scheme="$diff_s_context"
;;
-p | --show-c-function )
diff_scheme="$diff_s_context"
;;
-u | -U* | --unified | --unified=* )
diff_scheme="$diff_s_unified"
;;
--normal )
diff_scheme="$diff_s_normal"
;;
-e | --ed )
diff_scheme="$diff_s_ed"
;;
-n | --rcs )
diff_scheme="$diff_s_rcs"
;;
-y | --side-by-side )
diff_scheme="$diff_s_sidebyside"
;;
esac
args+=( "$1" )
;;
esac
shift
done
args+=( "$@" )
# Validate the option and re-define the function behavior
case "$CDIFF_WHEN" in
always )
# Do nothing over defined by options
;;
never )
# Skip colorizing
diff_scheme=""
;;
auto )
# Check if piped
test -t 1 || diff_scheme=""
;;
* )
echo "Invalid argument '$CDIFF_WHEN' for --color" >&2
return 2
;;
esac
[ -z "$diff_scheme" ] && {
env diff "${args[@]}"
return $?
}
env diff "${args[@]}" | sed "$diff_scheme; s/\$/$diff_c_reset/"
return $PIPESTATUS
}
# =========================================================================
# EOF