Browse Source

ansi colors

main
pvincent 5 days ago
parent
commit
bdf977e78a
  1. 33
      bin/miaou-bash
  2. 42
      bin/pkg_add
  3. 2
      etc/bash.bashrc
  4. 155
      lib/ansi.bash
  5. 11
      lib/initiate.bash
  6. 2
      lib/uninstall.bash
  7. 0
      test/attic/associative.bash
  8. 0
      test/attic/tee_last_line.bash
  9. 70
      test/miaou-bash.test.bash
  10. 80
      test/stub/ansi_error.bash
  11. 2
      tools/idem_apt_install

33
bin/miaou-bash

@ -7,38 +7,7 @@ if [[ ! -v MIAOU_BASH_LOADED_ONCE ]]; then
readonly MIAOU_BASH_LOADED_ONCE=true
readonly MIAOU_DEBUG=${MIAOU_DEBUG:-false}
readonly MIAOU_ERROR_REGEX='^(.*): line ([0-9]+): miaou_error:'
declare -A ANSI
ANSI[RESET]=0
ANSI[STYLE_ITALIC]=3
ANSI[STYLE_BOLD]=1
ANSI[STYLE_UNDERLINE]=4
ANSI[STYLE_STRIKETHROUGH]=9
ANSI[FG_BLACK]=30
ANSI[FG_RED]=31
ANSI[FG_GREEN]=32
ANSI[FG_YELLOW]=33
ANSI[FG_BLUE]=34
ANSI[FG_PURPLE]=35
ANSI[FG_CYAN]=36
ANSI[FG_WHITE]=37
ANSI[FG_GRAY]=90
ANSI[FG_LIGHTBLUE]=94
ANSI[FG_MAGENTA]=95
ANSI[BG_BLACK]=40
ANSI[BG_RED]=41
ANSI[BG_GREEN]=42
ANSI[BG_YELLOW]=43
ANSI[BG_BLUE]=44
ANSI[BG_PURPLE]=45
ANSI[BG_CYAN]=46
ANSI[BG_WHITE]=47
ANSI[BG_GRAY]=90
ANSI[BG_MAGENTA]=95
readonly ANSI
source "$MIAOU_BASH_DIR"/lib/ansi.bash
fi
## functions

42
tools/pkg_add → bin/pkg_add

@ -2,22 +2,30 @@
# CONSTANTS
REFRESH_SLOT='24 hours'
DEBIAN_REPO_DIR='/var/lib/apt/lists/partial'
ARCH_REPO_DIR='/var/lib/pacman/sync'
# functions
function usage {
echo "usage: $(basename "$0") <packages...>"
echo 'idempotent package installation : update if necessary, install only if not yet done'
echo 'idempotent package installation : install only if not yet done + smart refresh repositories'
}
# $1 -> file to compare with REFRESH_SLOT
function needs_refresh {
[[ $(date --date="-$REFRESH_SLOT" +%s) -gt $(date -d "$(stat -c %y "$1")" +%s) ]]
}
function debian_update_repo {
if [ "$(date --date='-12 hours' +%s)" -gt "$(date -d "$(stat -c %y /var/lib/apt/lists/partial)" +%s)" ]; then
echo "updating repositoring..."
if needs_refresh "$DEBIAN_REPO_DIR"; then
echo "updating repositories..."
apt-get update
fi
}
function debian_add {
debian_update_repo
declare -a wanted=("$@")
declare -a missing=()
mapfile -t queries < <(dpkg-query -W -f='${Package}: ${Status}\n' "${wanted[@]}" 2>&1 || true)
@ -33,26 +41,38 @@ function debian_add {
done
if [[ "${#missing[@]}" -gt 0 ]]; then
debian_update_repo
echo installing packages: "${missing[@]}"
apt-get install -y "${missing[@]}"
fi
}
function arch_update_repo {
if needs_refresh "$ARCH_REPO_DIR"; then
echo "updating repositories..."
pacman -Syyu --noconfirm
fi
}
function arch_add {
pacman -Syyu --noconfirm
for i in "$@"; do
declare -a wanted=("$@")
declare -a missing=()
for i in "${wanted[@]}"; do
if ! pacman -Ql "$i" &>/dev/null; then
pacman -S --noconfirm "$i"
elif [ -n "${VERBOSE+x}" ] && $VERBOSE; then
echo "pacman package <$i> already installed!"
missing+=("$i")
fi
done
if [[ "${#missing[@]}" -gt 0 ]]; then
arch_update_repo
echo installing packages: "${missing[@]}"
pacman -S --noconfirm "${missing[@]}"
fi
}
# MAIN
[ "$UID" -ne 0 ] && echo 'root privilege required' && exit 2
[[ $# -lt 1 ]] && usage && builtin exit 1
[ "$UID" -ne 0 ] && echo 'root privilege required' && builtin exit 1
[[ $# -lt 1 ]] && usage && builtin exit 2
source "$MIAOU_BASH_DIR"/lib/functions.bash
DISTRO_LIKE=$(get_distro_like)

2
etc/bash.bashrc

@ -139,7 +139,9 @@ COLOR_OPTIONS=('--color=auto')
alias ls="ls ${COLOR_OPTIONS[*]}"
alias ll="ls ${COLOR_OPTIONS[*]} -l"
alias la="ls ${COLOR_OPTIONS[*]} -la"
alias lt="ls ${COLOR_OPTIONS[*]} -lt"
alias ltr="ls ${COLOR_OPTIONS[*]} -ltr"
alias l.="ls ${COLOR_OPTIONS[*]} -ld .*"
alias grep="grep ${COLOR_OPTIONS[*]}"
alias fgrep="fgrep ${COLOR_OPTIONS[*]}"
alias egrep="egrep ${COLOR_OPTIONS[*]}"

155
lib/ansi.bash

@ -1,120 +1,35 @@
# CONSTANTS
# MARGIN=0
PADDING=0
ANSI_RESET='\e[0m'
BLOCK_CONTINUATION_COL=
BLOCK_CONTINUATION_SYMBOL=
declare -A STYLE_VARIANTS
STYLE_VARIANTS[BOLD]='\e[1m'
STYLE_VARIANTS[ITALIC]='\e[3m'
STYLE_VARIANTS[UNDERLINE]='\e[4m'
STYLE_VARIANTS[STRIKETHROUGH]='\e[9m'
declare -A FG_COLORS
FG_COLORS[BLACK]='\e[30m'
FG_COLORS[RED]='\e[31m'
FG_COLORS[GREEN]='\e[32m'
FG_COLORS[YELLOW]='\e[33m'
FG_COLORS[BLUE]='\e[34m'
FG_COLORS[PURPLE]='\e[35m'
FG_COLORS[CYAN]='\e[36m'
FG_COLORS[WHITE]='\e[37m'
FG_COLORS[GRAY]='\e[90m'
FG_COLORS[MAGENTA]='\e[95m'
declare -A BG_COLORS
BG_COLORS[BLACK]='\e[40m'
BG_COLORS[RED]='\e[41m'
BG_COLORS[GREEN]='\e[42m'
BG_COLORS[YELLOW]='\e[43m'
BG_COLORS[BLUE]='\e[44m'
BG_COLORS[PURPLE]='\e[45m'
BG_COLORS[CYAN]='\e[46m'
BG_COLORS[WHITE]='\e[47m'
# BG_COLORS["GRAY"]='\e[90m'
# BG_COLORS["MAGENTA"]='\e[95m'
# FUNCTIONS
function ansi_reset {
PADDING=0
unset PADDING_LEFT PADDING_RIGHT BG FG
}
function ansi_length {
local clean_text
clean_text=$(echo "$1" | sed 's/\x1B\[[0-9;]*m//g')
echo "${#clean_text}"
}
function style_padding {
local left right
local padding_left=${PADDING_LEFT:-$PADDING}
local padding_right=${PADDING_RIGHT:-$PADDING}
left=$(for _ in $(seq 1 "${padding_left}"); do builtin echo -n ' '; done)
style_ansi "$left"
style_ansi "$1"
right=$(for _ in $(seq 1 "${padding_right}"); do builtin echo -n ' '; done)
style_ansi "$right"
}
function _style_fg {
[[ -n $1 ]] && echo "${FG_COLORS[$1]}" || true
}
function _style_variant {
[[ -n $1 ]] && echo "${STYLE_VARIANTS[$1]}" || true
}
function _style_bg {
if [[ -n "$1" ]]; then
[[ -v BG_COLORS[$1] ]] && echo "${BG_COLORS[$1]}" || >&2 echo "unknown BG value: $1"
fi
}
function style_ansi {
local fg bg variant
fg=$(_style_fg "${FG:-}") && [[ -n $fg ]] && builtin echo -en "$fg"
bg=$(_style_bg "${BG:-}") && [[ -n $bg ]] && builtin echo -en "$bg"
variant=$(_style_variant "${STYLE:-}") && [[ -n $variant ]] && builtin echo -en "$variant"
builtin echo -en "$1"
[[ -n $fg || -n $bg || -n $variant ]] && builtin echo -en "$ANSI_RESET"
true
}
function style_block {
local content length top bottom i
content=$(style_padding "$1")
length=$(ansi_length "$content")
top=$(
builtin echo -n "╔"
for _ in $(seq 1 "${length}"); do builtin echo -n '═'; done
builtin echo "╗"
)
style_ansi "${top}" && builtin echo
style_ansi "║"
style_ansi "${content}"
style_ansi "║" && builtin echo
bottom=$(
builtin echo -n "╚"
for i in $(seq 1 "${length}"); do
if [[ $i == "$BLOCK_CONTINUATION_COL" ]]; then
builtin echo -n "$BLOCK_CONTINUATION_SYMBOL"
else
builtin echo -n '═'
fi
done
builtin echo "╝"
)
style_ansi "${bottom}" && builtin echo
}
# TODO: build a global `declare -A __MIAOU_LOADED_LIBRARIES`
if [[ ! -v ANSI ]]; then
declare -A ANSI
ANSI[RESET]=0
ANSI[STYLE_ITALIC]=3
ANSI[STYLE_BOLD]=1
ANSI[STYLE_UNDERLINE]=4
ANSI[STYLE_STRIKETHROUGH]=9
ANSI[FG_BLACK]=30
ANSI[FG_RED]=31
ANSI[FG_GREEN]=32
ANSI[FG_YELLOW]=33
ANSI[FG_BLUE]=34
ANSI[FG_PURPLE]=35
ANSI[FG_CYAN]=36
ANSI[FG_WHITE]=37
ANSI[FG_GRAY]=90
ANSI[FG_LIGHTBLUE]=94
ANSI[FG_MAGENTA]=95
ANSI[BG_BLACK]=40
ANSI[BG_RED]=41
ANSI[BG_GREEN]=42
ANSI[BG_YELLOW]=43
ANSI[BG_BLUE]=44
ANSI[BG_PURPLE]=45
ANSI[BG_CYAN]=46
ANSI[BG_WHITE]=47
ANSI[BG_GRAY]=90
ANSI[BG_MAGENTA]=95
readonly ANSI
fi

11
lib/initiate.bash

@ -15,12 +15,8 @@ readonly BACKUP_SUFFIX='.ORIGINAL'
# FUNCTIONS
function usr_bin_miaou_bash {
ln -sf "$MIAOU_BASH_DIR/bin/miaou-bash" /usr/bin/miaou-bash
}
function install_required_packages {
. "$MIAOU_BASH_DIR"/tools/pkg_add "${REQUIRED_PKGS[@]}"
. "$MIAOU_BASH_DIR"/bin/pkg_add "${REQUIRED_PKGS[@]}"
}
function fix_users_bashrc {
@ -198,8 +194,9 @@ function ghostty_global_config {
done
}
function link_miaou_bash {
function link_bin_scripts {
sudo ln -sf "$MIAOU_BASH_DIR"/bin/miaou-bash /usr/bin/
sudo ln -sf "$MIAOU_BASH_DIR"/bin/pkg_add /usr/bin/
}
function create_sudoers {
@ -263,7 +260,7 @@ fix_terminfo_ghostty
fix_locales
ghostty_global_config
link_config_files
link_miaou_bash
link_bin_scripts
create_sudoers
fetch_mode
apply_mode

2
lib/uninstall.bash

@ -22,10 +22,10 @@ function uninstall {
sudo rm -f /etc/profile.d/20-miaou-bash.sh # deprecated: for older version
sudo rm -f /etc/profile.d/zz-miaou-bash.sh
sudo rm -f /etc/vimrc/vimrc.{azerty,core,local,miaou,rookie}
sudo rm -f /usr/bin/miaou-bash
sudo rm -f /etc/sudoers.d/miaou-bash
sudo rm -rf /var/lib/miaou-bash
sudo rm -r /etc/bash_completion.d/miaou-bash.complete
sudo rm -f /usr/bin/{miaou-bash,pkg_add}
sudo rm -rf /opt/miaou-bash
cat <<EOF

0
test/stub/associative.bash → test/attic/associative.bash

0
test/stub/tee_last_line.bash → test/attic/tee_last_line.bash

70
test/miaou-bash.test.bash

@ -8,15 +8,47 @@ TMP_ERR="$TMP_DIRECTORY/_stderr"
TMP_MIAOU="$TMP_DIRECTORY/_miaou"
readonly TMP_DIRECTORY TMP_OUT TMP_ERR TMP_MIAOU
source "$MIAOU_BASH_DIR"/lib/ansi.bash # ANSI associative array
# functions
function ansi_code {
local code
for code in "$@"; do
if [[ -v ANSI[$code] ]]; then
echo -ne "\e[${ANSI[$code]}m"
else
>&2 echo "unknown ansi key: <$code>"
break
fi
done
}
function print_passed {
ansi_code FG_GREEN
echo -en '.'
ansi_code RESET
}
function print_bypassed {
ansi_code FG_YELLOW
echo -en 'b'
ansi_code RESET
}
function print_failed {
ansi_code FG_RED
echo -en 'F'
ansi_code RESET
}
function assert_tty {
((test_count++))
if [[ -w /dev/tty ]]; then
echo -n .
print_passed
return
fi
echo -n F
print_failed
failures+=("test -w /dev/tty")
return 1
}
@ -26,11 +58,11 @@ function assert {
cmd="$MIAOU_BASH_DIR/test/stub/scenario.bash $1"
if $cmd >/dev/null 2>/dev/null 3>"$TMP_MIAOU"; then
if [[ -s "$TMP_MIAOU" ]]; then
echo -n .
print_passed
return
fi
fi
echo -n F
print_failed
failures+=("$cmd")
return 1
}
@ -48,11 +80,11 @@ function assert_grep {
cmd="$MIAOU_BASH_DIR/test/stub/scenario.bash $script"
if $cmd "${extra_args[@]}" >"$TMP_OUT" 2>/dev/null 3>/dev/null; then
if grep -q "$content" "$TMP_OUT"; then
echo -n .
print_passed
return
fi
fi
echo -n F
print_failed
#TODO: quote extra_args
for i in "${extra_args[@]}"; do safe_args+=$(printf '%q ' "$i"); done
failures+=("$cmd $safe_args")
@ -78,7 +110,7 @@ function fail_type {
if ! $locale_found; then
((test_bypassed++))
BYPASSED_LOCALES["$TEST_LANG"]=true
echo -n b
print_bypassed
return
fi
fi
@ -95,10 +127,10 @@ function fail_type {
fi
if [[ $success == true ]]; then
echo -n .
print_passed
return
fi
echo -n F
print_failed
failures+=("${lang_options[*]} $cmd")
return 1
}
@ -109,11 +141,11 @@ function fail_stderr_grep {
stderr_line="$2"
if ! $cmd 2>"$TMP_ERR" && grep -q "$stderr_line" "$TMP_ERR"; then
echo -n .
print_passed
return
fi
echo -n F
print_failed
failures+=("$cmd 2>&1 | grep -q \"$stderr_line\"")
return 1
}
@ -126,12 +158,12 @@ function fail_stderr_count {
if ! $cmd >/dev/null 2>"$TMP_ERR" 3>/dev/null; then
mapfile -t stderr <"$TMP_ERR"
if [[ "${#stderr[@]}" == "$count" ]]; then
echo -n .
print_passed
return
fi
fi
echo -n F
print_failed
failures+=("$cmd 2>$TMP_ERR")
return 1
}
@ -144,12 +176,12 @@ function fail_miaou_count {
if ! $cmd >/dev/null 2>/dev/null 3>"$TMP_MIAOU"; then
mapfile -t miaou <"$TMP_MIAOU"
if [[ "${#miaou[@]}" == "$count" ]]; then
echo -n .
print_passed
return
fi
fi
echo -n F
print_failed
failures+=("$cmd 3>$TMP_MIAOU")
return 1
}
@ -159,7 +191,7 @@ function assert_locales_equally_populated {
local locales_dir="$MIAOU_BASH_DIR"/locales
local main_locale_file="$locales_dir"/"$1".sh
if [[ ! -f $main_locale_file ]]; then
echo -n F
print_failed
failures+=("missing locale file: $main_locale_file for locale: $1")
return 1
fi
@ -169,7 +201,7 @@ function assert_locales_equally_populated {
for extra in "$@"; do
extra_locale_file="$locales_dir"/"$extra".sh
if [[ ! -f $extra_locale_file ]]; then
echo -n F
print_failed
failures+=("missing locale file: $extra_locale_file for locale: $extra")
return 1
fi
@ -181,7 +213,7 @@ function assert_locales_equally_populated {
if [[ ${#difference[@]} -gt 0 ]]; then
variable_name=$(echo "${difference[1]}" | cut -d' ' -f2)
change_type="${difference[0]}"
echo -n F
print_failed
if [[ "$change_type" =~ d ]]; then
failures+=("locale=$extra (in $extra_locale_file) misses variable (to add): ${variable_name}")
else
@ -191,7 +223,7 @@ function assert_locales_equally_populated {
fi
done
fi
echo -n .
print_passed
}
# main

80
test/stub/ansi_error.bash

@ -1,80 +0,0 @@
#!/usr/bin/env miaou-bash
code=118
failure_type='SUBSHELL TERM'
failure_payload='blabla'
stack_sources=('file.bash' './lib/utility.bash' 'file.bash')
stack_lines=(18 56 12)
stack_functions=('f2' 'f1' '<main>')
# function ansi_block_continuation {
# tput sc
# tput cuu 1
# BG=RED FG=BLACK style_ansi "\t╦"
# tput rc
# FG=RED style_ansi "\t║\n"
# }
function ansi_block_error {
BG=RED
FG=BLACK
PADDING=2
local content detail exit_code
content=$(style_ansi "$1")
content=$(style_padding "$content")
detail=$(BG=BLACK style_padding "$2")
exit_code=$(FG=CYAN style_ansi " $3")
BLOCK_CONTINUATION_COL=5 style_block "$content\t$detail$exit_code"
ansi_reset
FG=RED style_ansi " ║"
echo
}
function ansi_traces {
local optional location sources lines functions
declare -n sources=$1
declare -n lines=$2
declare -n functions=$3
optional=false
for i in "${!sources[@]}"; do
FG=RED style_ansi " ╟─⟶"
location=$(printf "%40s" "${sources[$i]}:${lines[$i]}")
is_true "$optional" &&
location=$(FG=GRAY STYLE=ITALIC style_ansi "$location") ||
location=$(STYLE=ITALIC style_ansi "$location")
builtin echo "${location} ${functions[$i]}"
optional=true
done
}
function show_error {
>&2 printf '\r'
>&2 ansi_block_error "$failure_type" "$failure_payload" "$code"
>&2 ansi_traces stack_sources stack_lines stack_functions
}
function echo_return {
echo TEXT &&
return 11
return 1
}
declare -F style_ansi >/dev/null || source "$MIAOU_BASH_DIR/lib/ansi.bash"
echo START
if echo_return; then
echo YOUPI
else
echo ERROR
fi
if echo_return; then
echo YOUPI
else
echo ERROR
fi
# echo -n "standard flow:" && show_error
# echo -n "starting subshell: $(show_error)"

2
tools/idem_apt_install

@ -1,5 +1,7 @@
#!/usr/bin/env miaou-bash
# FIXME: deprecated, to be removed in a few months!
function usage {
local BASECMD
BASECMD=$(basename "$0")

Loading…
Cancel
Save