#!/usr/bin/env bash readonly BASE_PATH="${STYLER_DATA_PATH:-${XDG_DATA_HOME:-$HOME/.local/share}/styler}" readonly CACHE_PATH="${STYLER_CACHE_PATH:-${XDG_CACHE_HOME:-$HOME/.cache}/styler}" readonly PACKAGE_PATH="$BASE_PATH/packages" readonly PROCESSOR_PATH="$BASE_PATH/processors" readonly VERSION="0.4.1" main() { local cmd="" local ret=0 case "$1" in theme) cmd="switch_theme" ;; set) cmd="set_theme" ;; list) cmd="list" ;; download) cmd="download" ;; -v | --version | version) printf "Program theming script.\n\n©Marty Oehme\n\nVersion: %s\n" "$VERSION" exit 0 ;; -h | --help | help | *) cmd="usage" ;; esac shift $cmd "$@" ret=$((ret + $?)) exit $ret } usage() { printf "%s\n" \ "" \ " styler Quickly switch your linux style." \ " Uses base16 themes to quickly set them for a variety of applications." \ "" \ " Usage: styler [-hv][set|theme base16-themename]" \ "" \ " Arguments:" \ "" \ " theme [theme] Temporarily switch theme. Use any valid base16 theme name (without base16- prefix)." \ " Theme will be lost upon restart, or application restarts." \ "" \ " set [theme] Set the theme. Use any valid base16 theme name (without base16- prefix)." \ " Same as 'theme' option, but changes will be made permanent." \ "" \ " list [processors|packages|themes] Print out all currently installed processors, templates, or themes from selection. " \ " list themes Will print out themes which are available for all installed packages." \ " list themes all Will print out every single installed theme, regardless of its universal availability." \ "" \ " download [username/repository] Download a base16 template into the package directory or download a processor16" \ " into the processor directory. Use user/repo format to automatically pull from github." \ "" \ " help | -h | --help Print out this help." \ "" \ " version | -v | --version Print out program information." \ "" \ "" } # base directory should always exist base_dir_exists_or_create() { [[ -d "$BASE_PATH" ]] || mkdir "$BASE_PATH" [[ -d "$PACKAGE_PATH" ]] || mkdir "$PACKAGE_PATH" [[ -d "$PROCESSOR_PATH" ]] || mkdir "$BASE_PATH" } # retrieves all relevant packages from BASE_PATH/packages # 'relevant' here means they follow github pattern of author/repository get_packages() { for author in "$PACKAGE_PATH"/*; do # TODO should eventually be used to either distinguish between author/pkg and pkg packages # or to spit out a warning if they should not be used. # if grep -q -e '^base16-' <<<"$(basename -- "$author")"; then # echo ERROR # fi for package in "$author"/*; do [[ -e "$author" ]] || break [[ -d "$package" ]] || break printf "%s/%s\n" "$(basename -- "$author")" "$(basename -- "$package")" done done } # retrieves all processors from BASE_PATH/processors # 'relevant' here means they follow github pattern of author/repository get_processors() { for author in "$PROCESSOR_PATH"/*; do for package in "$author"/*; do for processor in "$package"/*; do [[ -e "$processor" ]] || break [[ -f "$processor" ]] || break if grep -q -e '/theme_[[:alnum:]]\{1,\}$' <<<"$processor"; then printf "%s\n" "$(basename -- "$processor")" fi done done done } # retrieves all installed themes available to all packages # can be called with `all` argument to return themes which only exist for some packages get_themes() { local themes themes=$(find "$PACKAGE_PATH" -type f -name 'base16-*') local filtered if [[ "$1" = "all" ]]; then filtered="$(get_unique_themes "$themes")" else filtered="$(get_universal_themes "$themes")" fi echo "$filtered" } # filter down the list passed in to keep repeating base16 themes only once per name # and remove surrounding path information (and file ending) get_unique_themes() { if _cache_invalid "$CACHED_UNIQUE_THEMES" "$PACKAGE_PATH"; then notify-send "styler cache invalid" "rebuilding cache will take a moment..." _cache_rebuild "$CACHED_UNIQUE_THEMES" _fetch_unique_themes "$@" else cat "$CACHED_UNIQUE_THEMES" fi } readonly CACHED_UNIQUE_THEMES=${CACHE_PATH}/unique_themes # returns themes which are available in all installed packages # returns cached version or rebuilds cache get_universal_themes() { if _cache_invalid "$CACHED_UNIVERSAL_THEMES" "$PACKAGE_PATH"; then notify-send "styler cache invalid" "rebuilding cache will take a moment..." _cache_rebuild "$CACHED_UNIVERSAL_THEMES" _fetch_universal_themes "$@" else cat "$CACHED_UNIVERSAL_THEMES" fi } readonly CACHED_UNIVERSAL_THEMES=${CACHE_PATH}/universal_themes _fetch_unique_themes() { echo "$1" | sed "s/.*\\/base16-//;s/\\..*//" | sort | uniq } # only keep themes which are available in every installed package _fetch_universal_themes() { local themes="$1" local unique unique="$(get_unique_themes "$themes")" local packages packages="$(get_packages)" local filtered for t in $unique; do for p in $packages; do if ! echo "$themes" | grep -qe "^.*$p.*$t.*$" -; then themes="$(echo "$themes" | sed "/$t/d")" unique="$(echo "$unique" | sed "/$t/d")" break fi done done echo "$unique" } # returns true if changes to $2 are newer than $1 # used to pass in cache file for $1, what it is caching for $2 _cache_invalid() { cache_upd="$(_last_update "$1")" live_upd="$(_last_update "$2")" if [ "${live_upd%.*}" -gt "${cache_upd%.*}" ]; then true else false fi } # recreates target cache with output of function # arguments: "target_cache_file" "function_name" "arguments_for_function" _cache_rebuild() { local target="$1" shift local fct="$1" shift local new new="$($fct "$@")" mkdir -p "$CACHE_PATH" echo "$new" | tee "$target" } # returns time of last update to argument passed in # argument can be path to file or directory (searched recursively) # if argument does not exist gives 0 _last_update() { [[ ! -e "$1" ]] && echo 0 && return find "$1" -type f -printf "%T@\n" | sort -n | tail -n 1 } # temporarily switch theme, same thing as setting, only with permanence flag turned off for processors switch_theme() { set_theme "$1" "false" } # call processors for all installed packages set_theme() { local theme="$1" if [[ -z $theme ]]; then printf "Theme application requires an argument.\n" exit 1 fi local permanent="${2:-true}" local packages packages="$(get_packages)" if [[ -z "$packages" ]]; then printf "ERROR: No base16 packages installed. Please install at least 1 base16 package in %s/.\n" "$PACKAGE_PATH" >&2 exit 1 fi local processors processors="$(get_processors)" if [[ -z "$processors" ]]; then printf "ERROR: No application processors installed. Please install at least one processor in %s/.\n" "$PROCESSOR_PATH" >&2 exit 1 fi for pkg in $packages; do local appext # filter the application a package targets, since base16 packages # carry standard names this removes everything before base16- # the result is the application it targets # shellcheck disable=SC2001 appext=$(sed -e 's|^[A-Za-z0-9-]\{1,\}/base16-||' <<<"$pkg") # Compares application extension with existing processors and runs the appropriate processor if found processor=$(find "$PROCESSOR_PATH" -type f | grep -e "theme_$appext") if [[ -f "$processor" ]]; then "$processor" "$PACKAGE_PATH" "$pkg" "$theme" "$permanent" else printf "WARN: No processor found for application %s in %s. Make sure you install a processor for the application.\n" "$appext" "$PROCESSOR_PATH/" >&2 fi done } list() { local selected="$1" shift case "$selected" in packages) get_packages ;; processors) get_processors ;; themes) get_themes "$@" ;; *) echo "Please select one of packages | processors | themes to list." ;; esac } download() { local pkg="$1" local page="https://github.com" local repo="$page/$pkg" [[ -z "$pkg" ]] && { echo "No package to download passed in. Please provide a package to download in the form user/repository." exit 1 } type git >/dev/null 2>&1 || { echo "git is required to clone base16 package. Please install git." exit 1 } base_dir_exists_or_create if ! git ls-remote --exit-code -h "$repo" >/dev/null; then echo "Repository $repo not found." exit 1 fi # if package has patter name/base16-program, put it in packages; if name/process16-program put it in processors # if none of the above, assume it's a processor but warn the user if grep -q -e '^[0-9A-Za-z-]\{1,\}/base16-[0-9A-Za-z-]\{1,\}$' <<<"$pkg"; then git clone "$repo" "$PACKAGE_PATH/$pkg" elif grep -q -e '^[0-9A-Za-z-]\{1,\}/process16-[0-9A-Za-z-]\{1,\}$' <<<"$pkg"; then git clone "$repo" "$PROCESSOR_PATH/$pkg" else echo "Package does not fit default naming scheme of packages/processors. Assuming it is a processor but please check manually." git clone "$repo" "$PROCESSOR_PATH/$pkg" fi } main "$@"