Skip to content

Instantly share code, notes, and snippets.

@rasmusmerzin
Created April 26, 2020 18:42
Show Gist options
  • Save rasmusmerzin/8ca247848e13a07c1bf9ca25ee620229 to your computer and use it in GitHub Desktop.
Save rasmusmerzin/8ca247848e13a07c1bf9ca25ee620229 to your computer and use it in GitHub Desktop.
#!/bin/bash
INDENT=4
USAGE='[q] quit [j/k] move [G] submit [h/l] select [H/L] jump [A] select all'
DESC_HEIGHT=2
err() { echo "error: $*" >&2 && exit 1; }
is_installed() { command -v "$*" >/dev/null; }
# path
get_file_directory() { echo "$*" | sed 's/^\(.*\)\/[^\/]\+\/\?$/\1/'; }
get_file_name() { echo "$*" | sed 's/^.*\/\([^\/]\+\)\/\?$/\1/'; }
get_file_depth() {
steps=$(echo "$*" | sed 's/\/$//;s/[^\/]//g')
echo ${#steps}
}
missingdeps=
for dep in sed grep tr seq sudo
do is_installed "$dep" || missingdeps="$dep $missingdeps"
done
if [ -n "$missingdeps" ]
then err "required but not installed: $missingdeps"
fi
[ "$EUID" = 0 ] && err "this script shouldn't be run as root"
# arr
join() { tr ' ' ' '; }
split() { tr ' ' ' '; }
nth() {
target=$1; shift
[ $target -lt 0 ] && target=$(($#+target))
i=0
for val in "$@"
do
[ $i -eq $target ] && { echo $val; break; }
i=$((i+1))
done
}
rename() {
target=$1; shift
new_value=$1; shift
[ $target -lt 0 ] && target=$(($#+target))
new_arr=
i=0
for val in "$@"
do
if [ $i -eq $target ]
then new_arr="$new_arr $new_value"
else new_arr="$new_arr $val"
fi
i=$((i+1))
done
echo "$new_arr"
}
inside() {
key=$(echo "$1" | split); shift
is=0
for val in "$@"
do [ "$(echo "$val" | split)" = "$key" ] && is=1 && break
done
test $is -eq 1
}
# pkg
if is_installed pacman
then
pkg_update='command pacman -Syu --noconfirm'
pkg_add='command pacman -S --noconfirm --needed'
pkg_is='command pacman -Si >/dev/null 2>/dev/null'
export pkg_update pkg_add pkg_is
elif is_installed apt-get
then
pkg_update='command apt-get update -y && command apt-get upgrade -y'
pkg_add='command apt-get install -y'
pkg_is='command apt-get show >/dev/null 2>/dev/null'
export pkg_update pkg_add pkg_is
else err 'currently supported package managers: apt-get; pacman -- of which none are installed'
fi
# config
pull_config() {
domain=$1
author=$2
config=$3
{ [ -d "$HOME/.config/$config" ] || [ -f "$HOME/.config/$config" ]; } && return
mkdir -p ~/.config
cd ~/.config || return
git clone "https://$domain/$author/config-$config" || return
mv "config-$config" "$config"
cd "$config" || return
[ -f 'init.sh' ] && ./init.sh
}
# super
super() {
while [ -z "$pass" ] || ! echo "$pass" | command sudo -S -- sh -c 'echo 2>/dev/null'
do
printf "[sudo] password for $USER: "
printf '\033[8m' && pass=$(sed 'q' /dev/stdin)
printf '\033[28m'
echo
done
echo "$pass" | command sudo -S -- sh -c "$*";
}
init_module_list() {
cd "$(get_file_directory "$0")" || err "couldn't access module list"
cursor=0
modules=$(find src/* | sed 's/+$//' | sort -u | join);
modules_name=
modules_depth=
modules_selected=
modules_count=0
max_width=0
for f in $modules
do modules_count=$((modules_count+1))
depth=$(($(get_file_depth $f)-1))
name=$(get_file_name $f | split)
[ ${#name} -gt $max_width ] && max_width=${#name}
modules_name="$modules_name $name"
modules_depth="$modules_depth $depth"
modules_selected="$modules_selected 0"
done
folds=
folds_count=0
for level in $(echo "$modules_depth" | tr ' ' '\n' | grep -v '^0$' | sort -u)
do
prev_index=0
for i in $(seq $((modules_count-1)))
do depth=$(nth $i $modules_depth)
if [ $depth -lt $level ]
then
[ $((i-prev_index)) -gt 2 ] && {
folds="$folds $((prev_index+1)) $((i-1))"
folds_count=$((folds_count+1))
}
prev_index=$i
elif [ $i -eq $((modules_count-1)) ]
then
folds="$folds $((prev_index+1)) $i"
folds_count=$((folds_count+1))
fi
done
done
}
should_render() {
bool=1
first=
for f in $folds
do
if [ -z $first ]
then first=$f
else
last=$f
[ $* -ge $first ] && [ $* -le $last ] &&
[ $(nth $((first-1)) $modules_selected) -eq 0 ] &&
bool=0 && break
first=
fi
done
[ $bool -ne 0 ]
}
get_render_index() {
index=$1
first=
for f in $folds
do
if [ -z $first ]
then first=$f
else
last=$f
decider=$((first-1))
[ $1 -ge $first ] && (should_render $decider) && [ $(nth $decider $modules_selected) -eq 0 ] && {
if [ $1 -le $last ]
then index=; break
else index=$((index-last+first-1))
fi
}
first=
fi
done
[ -n "$index" ] && echo $index
}
get_render_count() {
for i in $(seq $((modules_count-1)) -1 0)
do should_render $i && { get_render_index $i; break; }
done
}
get_body_start() {
echo $((2+DESC_HEIGHT))
}
render_usage() {
printf '\33[;H\33[2m%s\33[0m' "$USAGE"
}
render_description() {
printf '\33[2;H\33[K%b' "$(
name=$(nth $cursor $modules_name)
indent=$((max_width+3))
space=$(($(tput cols)-indent))
selected_module=$(nth $cursor $modules)
desc=$([ -f "$selected_module" ] && sed -n -e 's/\s\+/ /g' -e 's/^#\s*desc\s*:\s*\(.*\)\s*$/\1/p;/^[^#]/q' "$selected_module")
for i in $(seq $DESC_HEIGHT)
do
if [ $i -lt $DESC_HEIGHT ]
then printf '\33[K\33[%dC%s\n' $indent "$(echo "$desc" | cut -b "$(((i-1)*space+1))-$((i*space))")"
else
if [ $((space*DESC_HEIGHT)) -lt ${#desc} ]
then printf '\33[K\33[%dC%s...\n' $indent "$(echo "$desc" | cut -b "$(((i-1)*space+1))-$((i*space-4))")"
else printf '\33[K\33[%dC%s\n' $indent "$(echo "$desc" | cut -b "$(((i-1)*space+1))-$((i*space))")"
fi
fi
done
printf '\33[%dA%s:' $DESC_HEIGHT "$name"
)"
}
render_list() {
for i in $(seq 0 $((modules_count-1)))
do
{ [ $# -le 0 ] || inside $i $*; } && (should_render $i) &&
printf "\33[$(($(get_body_start)+$(get_render_index $i)));H\33[K\33[$(($(nth $i $modules_depth)*INDENT+1))C\33[D[?] %s" "$(nth $i $modules_name)"
done
}
render_selection() {
for i in $(seq 0 $((modules_count-1)))
do
{ [ $# -le 0 ] || inside $i $*; } && (should_render $i) && {
mark=' ' && [ $(nth $i $modules_selected) -ne 0 ] && mark='*'
printf "\33[$(($(get_body_start)+$(get_render_index $i)));$(($(nth $i $modules_depth)*INDENT+2))H%s" "$mark"
}
done
}
render_cursor() {
printf "\33[$(($(get_body_start)+$(get_render_index $cursor)));$(($(nth $cursor $modules_depth)*INDENT+2))H"
}
render_full() {
printf '\33[2J%b' "$(
render_usage
render_list
render_selection
render_description
render_cursor
)"
}
down() {
for i in $(seq $((cursor+1)) $((modules_count-1)))
do should_render $i && { cursor=$i; break; }
done
printf '%b' "$(render_description; render_cursor)"
}
last() {
for i in $(seq $((modules_count-1)) -1 0)
do should_render $i && { cursor=$i; break; }
done
printf '%b' "$(render_description; render_cursor)"
}
up() {
for i in $(seq $((cursor-1)) -1 0)
do should_render $i && { cursor=$i; break; }
done
printf '%b' "$(render_description; render_cursor)"
}
first() {
cursor=0
printf '%b' "$(render_description; render_cursor)"
}
select_module() {
modules_selected=$(rename $cursor 1 $modules_selected)
render_full
}
deselect_module() {
modules_selected=$(rename $cursor 0 $modules_selected)
render_full
}
toggle_module() {
current_state=$(nth $cursor $modules_selected)
current_state=$((1-current_state))
modules_selected=$(rename $cursor $current_state $modules_selected)
render_full
}
execute_modules() {
cd "$(get_file_directory "$0")" || err "couldn't access module list"
chosen_modules=
chosen_modules_name=
chosen_modules_count=0
chosen_modules_max_width=0
i=0
for f in $modules
do
{
[ -z "$modules_selected" ] ||
[ $(nth $i $modules_selected) -ne 0 ] && (should_render $i)
} && [ -f "$f" ] && {
chosen_modules="$chosen_modules $f"
chosen_modules_count=$((chosen_modules_count+1))
# name=$(nth $i $modules_name)
name=$(get_file_name $f | split)
chosen_modules_name="$chosen_modules_name $name"
[ ${#name} -gt $chosen_modules_max_width ] && chosen_modules_max_width=${#name}
}
i=$((i+1))
done
if [ -n "$chosen_modules" ]
then
cleanup
cleanup() { printf '\n\33[28m'; exit; }
echo "Executing modules:$chosen_modules"
super
execution_times=
for m in $chosen_modules
do
begin_time=$(date +%s)
{
if [ -n "$(cat "$m")" ]
then . "$m"
else super "$pkg_add $(get_file_name "$m")"
fi
} 2>&1 | sed "s|^|\o33[1m$m:\o33[0m|"
end_time=$(date +%s)
execution_times="$execution_times $((end_time-begin_time))"
done
echo
for i in $(seq 0 $((chosen_modules_count-1)))
do printf "%-$((chosen_modules_max_width+1))s%6ds\n" "$(nth $i $chosen_modules_name)" "$(nth $i $execution_times)"
done
true
else
cleanup
echo 'No modules selected'
false
fi
}
main() {
init_module_list
render_full
cols=$(tput cols)
lines=$(tput lines)
ansi_state=0
while read -rsn1 KEY
do
if [ "$(printf '\33')" = "$KEY" ]
then
if [ $ansi_state -eq 0 ]
then ansi_state=1
else ansi_state=0
fi
elif [ '[' = "$KEY" ]
then
if [ $ansi_state -eq 1 ]
then ansi_state=2
else ansi_state=0
fi
else
case "$KEY" in
q) cleanup && exit ;;
k) up ;;
j) down ;;
l) select_module ;;
h) deselect_module ;;
L) last ;;
H) first ;;
A)
if [ $ansi_state -eq 2 ]
then up
else
if inside 0 $modules_selected
then
modules_selected=
for i in $(seq $modules_count)
do modules_selected="$modules_selected 1"
done
else
modules_selected=
for i in $(seq $modules_count)
do modules_selected="$modules_selected 0"
done
should_render $cursor || up
fi
render_full
fi
;;
B) [ $ansi_state -eq 2 ] && down ;;
C) [ $ansi_state -eq 2 ] && select_module ;;
D) [ $ansi_state -eq 2 ] && deselect_module ;;
$(printf '\n')) toggle_module ;;
G)
if execute_modules
then break
else render_cursor
fi
;;
*) ;;
esac
ansi_state=0
fi
old_cols=$cols
old_lines=$lines
cols=$(tput cols)
lines=$(tput lines)
{ [ $old_cols != $cols ] || [ $old_lines != $lines ]; } && render_full
done
}
cleanup() { printf '\n\33[28m'; }
trap 'cleanup; exit' SIGINT SIGTERM
if [ $# -eq 0 ]
then
cleanup() { printf "\33[28m\33[$(($(get_body_start)+$(get_render_count)+2));H"; }
main
else
modules=$*
execute_modules
fi
pass=
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment