Improved style.sh
Better reverse video for multiline text.
Usage and help including option to list style options.
Visual test script in //scripts/tests
Also added an automated test and "expected" files
Change-Id: Ie095c31e01bc3ebdc384d3d13d5d7fb151f948ce
diff --git a/devshell/lib/style.sh b/devshell/lib/style.sh
index 15a4d6d..7d9264c 100644
--- a/devshell/lib/style.sh
+++ b/devshell/lib/style.sh
@@ -22,10 +22,14 @@
#
# export STYLE_WARNING="--stderr --faint --dark_red --background dark_yellow"
#
+# Visual tests (and demonstration of capabilities) can be run from:
+# //scripts/tests/style-test-visually
# This script should be sourced. It is compatible with Bash 3.
# MacOS still comes with Bash 3, so unfortunately no associative arrays.
+STYLE_TO_TTY_ONLY=false # Set to true to suppress styling if output is redirected
+
[[ "${STYLE_ERROR}" != "" ]] || STYLE_ERROR="--stderr --bold --color red"
[[ "${STYLE_WARNING}" != "" ]] || STYLE_WARNING="--stderr --bold --color dark_yellow"
[[ "${STYLE_INFO}" != "" ]] || STYLE_INFO="--stderr --bold --color dark_green"
@@ -58,6 +62,53 @@
declare -i TERM_COLORS__cyan=96
declare -i TERM_COLORS__white=97
+style::colors() {
+ set | sed -n "s/^TERM_COLORS__\([^=]*\)=.*$/\1/p" >&2
+}
+
+style::attributes() {
+ set | sed -n "s/^TERM_ATTRIBUTES__\([^=]*\)=.*$/--\1/p" >&2
+}
+
+style::usage() {
+ local help_option="$1"; shift
+ if [[ "${help_option}" == "colors" ]]; then
+ style::colors
+ return
+ elif [[ "${help_option}" == "attributes" ]]; then
+ style::attributes
+ return
+ fi
+ local function_call="$1"
+ local -a words=( $function_call )
+ local funcname="${words[0]}"
+ local command="$2"
+ local specifics="$3"
+
+ >&2 echo "
+Usage: ${function_call} [style options] [command parameters]"
+
+ if [[ "${specifics}" != "" ]]; then
+ >&2 echo "
+${specifics}"
+ fi
+ >&2 cat << EOF
+
+style options include:
+ --bold, --faint, --underline, etc.
+ --color <color_name>
+ --background <color_name>
+ --stderr (output to standard error instead of standard out)
+
+ echo "This is \$(style::echo -f --bold LOUD) and soft."
+
+command parameters are those supported by the ${command} command.
+
+Use ${funcname} --help colors for a list of colors or backgrounds
+Use ${funcname} --help attributes for a list of style attribute flags
+EOF
+}
+
style::attribute() {
local name="$1"
local fallback="$2"
@@ -98,10 +149,19 @@
echo $((10+${color}))
}
-_STYLE_RESET="\033[0m"
-
style::stylize() {
+ if [[ "$1" == --* || "$1" == "" ]]; then
+ style::usage "$2" "${FUNCNAME[0]} <command>" "stylized" "\
+<command> is any command with output to stylize, followed by style options,
+and then the command's normal parameters."
+ return
+ fi
+
local command="$1"; shift
+ if [[ "$1" == "--help" ]]; then
+ style::usage "$2" "style::${command}" "'${command}'"
+ return
+ fi
local get_flags=true
local -i fd=1
@@ -116,6 +176,10 @@
fd=2
shift
;;
+ --stdout)
+ fd=1
+ shift
+ ;;
--color)
shift; name="$1"; shift
styles="${styles}${semicolon}$(style::color $name || exit $?)" || return $?
@@ -150,33 +214,36 @@
esac
done
- local status=0
-
- if [ ! -t $fd ]; then
+ if [ ! -t ${fd} ] && ${STYLE_TO_TTY_ONLY}; then
# Output is not to a TTY so don't stylize
- >&${fd} "${command}" "$@" || status=$?
- return $status
+ >&${fd} "${command}" "$@" || return $?
+ return 0
fi
local if_newline=''
local text
+
# Add placeholder (.) so command substitution doesn't strip trailing newlines
- text="$("${command}" "$@" || exit $?;echo '.')" || status=$?
+ text="$("${command}" "$@" || exit $?;echo -n '.')" || return $?
+
local -i len=$((${#text}-2))
if [[ "${text:$len:1}" == $'\n' ]]; then
+ # Save last newline to add back after styling.
if_newline='\n'
else
((len++))
fi
- # Strip trailing newline, if any, and placeholder
- # Last newline should not be stylized.
- # TODO(richkadel): We may want to remove style from all newlines.
- # Background color looks odd when newlines are styled.
+ # Strip trailing newline, if any, and placeholder.
text="${text:0:$((len))}"
- >&${fd} printf "\033[${styles}m%s${_STYLE_RESET}${if_newline}" "${text}"
+ # Style everything except newlines, otherwise background color highlights
+ # entire line. Add extra line with a character so sed does not add it's own
+ # last newline, then delete the line after substitutions.
+ local styled=$(printf '%s\n.' "${text}" | sed -e $'s/$/\033[0m/;s/^/\033['"${styles}"'m/;$d')
- return $status
+ >&${fd} printf "%s${if_newline}" "${styled}"
+
+ return 0
}
style::echo() {
@@ -191,18 +258,32 @@
style::stylize "${FUNCNAME[0]:7}" "$@" || return $?
}
+style::_echo_with_styles() {
+ local funcname="$1";shift
+ local style_options="$1";shift
+ if [[ "$1" == "--help" ]]; then
+
+ style::usage "$2" "${funcname}" "echo" "\
+Default style options for ${funcname}:
+ $(style::echo ${style_options} --stdout \"${style_options}\")"
+
+ return
+ fi
+ style::echo ${style_options} "$@" || return $?
+}
+
style::error() {
- style::echo ${STYLE_ERROR} "$@" || return $?
+ style::_echo_with_styles "${FUNCNAME[0]}" "${STYLE_ERROR}" "$@" || return $?
}
style::warning() {
- style::echo ${STYLE_WARNING} "$@" || return $?
+ style::_echo_with_styles "${FUNCNAME[0]}" "${STYLE_WARNING}" "$@" || return $?
}
style::info() {
- style::echo ${STYLE_INFO} "$@" || return $?
+ style::_echo_with_styles "${FUNCNAME[0]}" "${STYLE_INFO}" "$@" || return $?
}
style::link() {
- style::echo ${STYLE_LINK} "$@" || return $?
+ style::_echo_with_styles "${FUNCNAME[0]}" "${STYLE_LINK}" "$@" || return $?
}
diff --git a/tests/expected/style-tests.err b/tests/expected/style-tests.err
new file mode 100644
index 0000000..b921ea7
--- /dev/null
+++ b/tests/expected/style-tests.err
@@ -0,0 +1,100 @@
+
+Usage: style::stylize <command> [style options] [command parameters]
+
+<command> is any command with output to stylize, followed by style options,
+and then the command's normal parameters.
+
+style options include:
+ --bold, --faint, --underline, etc.
+ --color <color_name>
+ --background <color_name>
+ --stderr (output to standard error instead of standard out)
+
+ echo "This is $(style::echo -f --bold LOUD) and soft."
+
+command parameters are those supported by the stylized command.
+
+Use style::stylize --help colors for a list of colors or backgrounds
+Use style::stylize --help attributes for a list of style attribute flags
+
+Usage: style::printf [style options] [command parameters]
+
+style options include:
+ --bold, --faint, --underline, etc.
+ --color <color_name>
+ --background <color_name>
+ --stderr (output to standard error instead of standard out)
+
+ echo "This is $(style::echo -f --bold LOUD) and soft."
+
+command parameters are those supported by the 'printf' command.
+
+Use style::printf --help colors for a list of colors or backgrounds
+Use style::printf --help attributes for a list of style attribute flags
+
+Usage: style::error [style options] [command parameters]
+
+Default style options for style::error:
+ [1;91m"--stderr --bold --color red"[0m
+
+style options include:
+ --bold, --faint, --underline, etc.
+ --color <color_name>
+ --background <color_name>
+ --stderr (output to standard error instead of standard out)
+
+ echo "This is $(style::echo -f --bold LOUD) and soft."
+
+command parameters are those supported by the echo command.
+
+Use style::error --help colors for a list of colors or backgrounds
+Use style::error --help attributes for a list of style attribute flags
+
+Usage: style::link [style options] [command parameters]
+
+Default style options for style::link:
+ [4;34m"--underline --color dark_blue"[0m
+
+style options include:
+ --bold, --faint, --underline, etc.
+ --color <color_name>
+ --background <color_name>
+ --stderr (output to standard error instead of standard out)
+
+ echo "This is $(style::echo -f --bold LOUD) and soft."
+
+command parameters are those supported by the echo command.
+
+Use style::link --help colors for a list of colors or backgrounds
+Use style::link --help attributes for a list of style attribute flags
+black
+blue
+cyan
+dark_blue
+dark_cyan
+dark_green
+dark_magenta
+dark_red
+dark_yellow
+default
+gray
+green
+light_gray
+magenta
+pink
+purple
+red
+white
+yellow
+--blink
+--bold
+--faint
+--italic
+--reset
+--underline
+[1;32mINFO: Info here[0m
+[1;33mWARNING: Watch out![0m
+[1;91mERROR: What went wrong now?[0m
+[5;33mWARNING: Customized warning style, still to stderr! :-)[0m
+
+This should still display in bold red, but on stderr
diff --git a/tests/expected/style-tests.out b/tests/expected/style-tests.out
new file mode 100644
index 0000000..87424d7
--- /dev/null
+++ b/tests/expected/style-tests.out
@@ -0,0 +1,39 @@
+[94m------------------[0m
+[94m------------------[0m
+[94m------------------[0m
+[94m----- colors -----[0m
+[94m--- attributes ---[0m
+[94m------------------[0m
+[1mstyle::echo --bold[0m
+[1;96mstyle::echo --bold --color cyan[0m
+[2;92mstyle::echo --faint --color green[0m
+[3;95mstyle::echo --italic --color magenta[0m
+[4;34mstyle::echo --underline --color dark_blue[0m
+[5;37mstyle::echo --blink --color light_gray[0m
+[95;46mstyle::echo --pink --background dark_cyan[0m
+[3;35mitalic this style may not work in some terminals: style::echo --italic --dark_magenta italic this style may not work in some terminals:[0m
+[1m Item Cost[0m
+[m ---- ----[0m
+[35;107m beans $ 2.90[0m
+[35;107m franks $ 9.35[0m
+[35;107m cola $ 7.99[0m
+[35;107m tiramasu $ 24.50[0m
+[43;30mNow is the time for all good[0m
+[43;30mpeople to come to the[0m
+[43;30maid of their country and world.[0m
+[4;34mhttp://wikipedia.com[0m
+STYLE_TO_TTY_ONLY=true
+
+This will not be styled. It doesn't print directly to the tty
+
+This will not be styled. stderr doesn't print directly to the tty
+[1;91mSTYLE_TO_TTY_ONLY=false[0m
+[96;2m[0m
+[96;2mThis will be styled even though it doesn't print directly to the tty.[0m
+[96;2m[0m
+[96;2mThis will be styled even though stderr doesn't print directly to the tty[0m
+This is [m-f --bold --yellow LOUD[0m and soft.
+This is [m--force --bold --yellow LOUD[0m and soft.
+This is [m--tty --bold --yellow LOUD[0m and soft.
+Bad style, Error status: 2
+No orange! Error status: 2
diff --git a/tests/style-test-visually b/tests/style-test-visually
new file mode 100755
index 0000000..876ea64
--- /dev/null
+++ b/tests/style-test-visually
@@ -0,0 +1,87 @@
+#!/bin/bash
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Visual tests for //scripts/devshell/lib/style.sh
+
+# This is not an automated unit test.
+# It prints stylized text describing the style to be shown, so a tester
+# can validate the expected style is rendered.
+
+# Note that some terminals do not support all terminal styles.
+# For instance, italic may not render as italic on MacOS.
+
+source "$(cd "$(dirname "${BASH_SOURCE[0]}")"/../devshell/lib >/dev/null 2>&1 && pwd)"/style.sh || exit $?
+
+runtest() {
+ command="$1"; shift
+ "${command}" "$@" "${command} $*"
+}
+
+style::stylize --help
+style::echo --blue $'------------------'
+style::printf --help
+style::echo --blue $'------------------'
+style::error --help
+style::echo --blue $'------------------'
+style::link --help
+style::echo --blue $'----- colors -----'
+style::echo --help colors
+style::echo --blue $'--- attributes ---'
+style::echo --help attributes
+style::echo --blue $'------------------'
+
+runtest style::echo --bold
+runtest style::echo --bold --color cyan
+runtest style::echo --faint --color green
+runtest style::echo --italic --color magenta
+runtest style::echo --underline --color dark_blue
+runtest style::echo --blink --color light_gray
+runtest style::echo --pink --background dark_cyan
+runtest style::echo --italic --dark_magenta italic "this style may not work in some terminals:"
+
+style::printf --bold '%10s %6s\n' Item Cost
+style::printf '%10s %6s\n' ---- ----
+style::printf --purple --background white '%10s $%6.2f\n' beans 2.90 franks 9.35 cola 7.99 tiramasu 24.50
+
+style::cat --background dark_yellow --black << EOF
+Now is the time for all good
+people to come to the
+aid of their country and world.
+EOF
+
+style::info 'INFO: Info here'
+style::warning 'WARNING: Watch out!'
+style::error 'ERROR: What went wrong now?'
+style::link 'http://wikipedia.com'
+
+STYLE_WARNING='--stderr --blink --dark_yellow'
+style::warning 'WARNING: Customized warning style, still to stderr! :-)'
+
+STYLE_TO_TTY_ONLY=true # default is false
+style::echo --bold --red "STYLE_TO_TTY_ONLY=$STYLE_TO_TTY_ONLY"
+style::echo --stderr --bold --red '
+This should still display in bold red, but on stderr' >/dev/null
+
+style::echo --color cyan --faint "
+This will not be styled. It doesn't print directly to the tty" | cat
+
+style::echo --stderr --color cyan --faint "
+This will not be styled. stderr doesn't print directly to the tty" 2>&1 | cat
+
+STYLE_TO_TTY_ONLY=false
+style::echo --bold --red "STYLE_TO_TTY_ONLY=$STYLE_TO_TTY_ONLY"
+style::echo --color cyan --faint "
+This will be styled even though it doesn't print directly to the tty." | cat
+
+style::echo --stderr --color cyan --faint "
+This will be styled even though stderr doesn't print directly to the tty" 2>&1 | cat
+
+# Three flags for the same thing:
+echo "This is $(style::echo -f --bold --yellow LOUD) and soft."
+echo "This is $(style::echo --force --bold --yellow LOUD) and soft."
+echo "This is $(style::echo --tty --bold --yellow LOUD) and soft."
+
+style::printf --blod --green 'Bad style' 2>/dev/null || echo "Bad style, Error status: $?"
+style::printf --faint --orange 'No orange' 2>/dev/null || echo "No orange! Error status: $?"
diff --git a/tests/style-tests b/tests/style-tests
new file mode 100755
index 0000000..3ddf5da
--- /dev/null
+++ b/tests/style-tests
@@ -0,0 +1,40 @@
+#!/bin/bash
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Automated tests for //scripts/devshell/lib/style.sh
+#
+# Usage: style-tests
+#
+# Returns: Error status if actual output does not match expected.
+
+TEST_NAME="$(basename "${BASH_SOURCE[0]}")"
+TESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
+
+verbose() {
+ echo
+ echo "======================================================="
+ echo
+ echo "$@"
+ echo
+ "$@"
+ echo
+}
+
+test_main() {
+ local expected_out="${TESTS_DIR}/expected/${TEST_NAME}.out"
+ local expected_err="${TESTS_DIR}/expected/${TEST_NAME}.err"
+ local capture_dir=$(mktemp -d)
+ local actual_out="${capture_dir}/${TEST_NAME}.out"
+ local actual_err="${capture_dir}/${TEST_NAME}.err"
+ ${TESTS_DIR}/style-test-visually 1> "${actual_out}" 2> "${actual_err}"
+
+ local status=0
+ verbose diff "${expected_out}" "${actual_out}" || status=$?
+ verbose diff "${expected_err}" "${actual_err}" || status=$?
+
+ return $status
+}
+
+test_main "$@" || return $?