diff --git a/README.md b/README.md index f66e91e..d65f705 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,10 @@ $ ln -s ~/dotfiles/tmux.conf ~/.tmux.conf ## Configure gnome-terminal +Install the [Hack font](https://github.com/source-foundry/Hack). + Run the following command: ```sh -$ ~/dotfiles/style-gnome-terminal.sh gruvbox8 +$ ~/dotfiles/style-gnome-terminal.sh styles/gruvbox8.conf ``` diff --git a/style-gnome-terminal.sh b/style-gnome-terminal.sh index 82276e9..27fe6f3 100755 --- a/style-gnome-terminal.sh +++ b/style-gnome-terminal.sh @@ -3,106 +3,246 @@ set -eu +# CONSTANTS +# ========= + +# DConf directory in which gnome-terminal profile configuration is stored. ROOT_DCONF_DIR='/org/gnome/terminal/legacy/profiles:/' -# ansi_colors COLOR1 ... COLOR16 -function ansi_colors() { - echo -n "['$1'" - for i in {2..16}; do - echo -n ",'${!i}'" +# The names of ANSI color properties in config files. +# Order matters. These correspond to the ANSI color codes. +ANSI_COLOR_PROPERTIES=( + "ansi-colors-black" + "ansi-colors-red" + "ansi-colors-green" + "ansi-colors-yellow" + "ansi-colors-blue" + "ansi-colors-purple" + "ansi-colors-cyan" + "ansi-colors-white" + "ansi-colors-bright-black" + "ansi-colors-bright-red" + "ansi-colors-bright-green" + "ansi-colors-bright-yellow" + "ansi-colors-bright-blue" + "ansi-colors-bright-purple" + "ansi-colors-bright-cyan" + "ansi-colors-bright-white" +) + +# LOGGING +# ======= + +# log ARGS... +# echo for stderr +function log() { + echo 1>&2 "$@" +} + +# die ARGS... +# log then exit with an error code +function die() { + log "$@" + exit 1 +} + + +# CONFIG +# ====== +# +# Configuration files have a very basic grammar: +# +# PROPERTY_NAME = VALUE +# +# The functions below allow reading config file contents. + +# config_get_property FILE PROPERTY +# Echoes the given property from the given config file. +function config_get_property() { + FILE="$1" + PROPERTY="$2" + + sed -n "s:^${PROPERTY} *= *\(\.*\):\1:p" < "${FILE}" +} + +# config_get_property_or_die FILE PROPERTY +# config_get_property, except dies if the property is not found. +function config_get_property_or_die() { + FILE="$1" + PROPERTY="$2" + + RESULT=$(config_get_property "${FILE}" "${PROPERTY}") + if [ -z "$RESULT" ]; then + die "Error: cannot find property '${PROPERTY}' in file '${FILE}'." + fi + + echo "${RESULT}" +} + +# config_get_font FILE +function config_get_font() { + FILE="$1" + config_get_property "${FILE}" "font" +} + +# config_get_foreground_color FILE +function config_get_foreground_color() { + FILE="$1" + config_get_property "${FILE}" "foreground-color" +} + +# config_get_background_color FILE +function config_get_background_color() { + FILE="$1" + config_get_property "${FILE}" "background-color" +} + +# join_ansi_colors COLOR1 ... COLOR16 +# Echoes "[${COLOR1}, ..., ${COLOR16}]". +function join_ansi_colors () { + echo -n "[$1" + for i in {2..16}; do + echo -n ", ${!i}" # Get the i-th argument to this function. done echo "]" } -# colorscheme NAME FG_COLOR BG_COLOR ANSI_COLORS -function colorscheme() { - NAME="$1" - FG_COLOR="$2" - BG_COLOR="$3" - ANSI_COLORS="$4" +# config_get_ansi_colors FILE +function config_get_ansi_colors() { + FILE="$1" + + ANSI_COLORS=() + for PROPERTY in "${ANSI_COLOR_PROPERTIES[@]}"; do + COLOR=$(config_get_property_or_die "${FILE}" "${PROPERTY}") + ANSI_COLORS+=("${COLOR}") + done - echo "${NAME};${FG_COLOR};${BG_COLOR};${ANSI_COLORS}" + join_ansi_colors "${ANSI_COLORS[@]}" } -function unpack_colorscheme_field(){ - PACKED="$1" - FIELD="$2" - echo "${PACKED}" | cut -d ';' -f "${FIELD}" + +# PROFILE +# ======= +# +# Gnome-terminal profile preferences are read and written through dconf. +# +# In the following functions, profiles are referenced by their dconf directory +# paths (ending in a '/' character). + +# profile_get_property PROFILE PROPERTY +# Echoes the given property from the given gnome-terminal profile. +function profile_get_property() { + PROFILE="$1" + PROPERTY="$2" + + dconf read "${PROFILE}${PROPERTY}" } -# colorscheme_name COLORSCHEME -function colorscheme_name() { - unpack_colorscheme_field "$1" 1 +# profile_set_property PROFILE PROPERTY VALUE +# Sets the given profile's given property to the given value. +function profile_set_property() { + PROFILE="$1" + PROPERTY="$2" + VALUE="$3" + + dconf write "${PROFILE}${PROPERTY}" "${VALUE}" } -# colorscheme_fg_color COLORSCHEME -function colorscheme_fg_color() { - unpack_colorscheme_field "$1" 2 +# profile_get_name PROFILE +# Echoes the human-readable name for the given gnome-terminal profile. +function profile_get_name() { + PROFILE="$1" + profile_get_property "${PROFILE}" "visible-name" } -# colorscheme_bg_color COLORSCHEME -function colorscheme_bg_color() { - unpack_colorscheme_field "$1" 3 +# profile_set_name PROFILE VALUE +# Sets the human-readable name for the given gnome-terminal profile. +function profile_set_name() { + PROFILE="$1" + VALUE="$2" + profile_set_property "${PROFILE}" "visible-name" "${VALUE}" } -# colorscheme_ansi_colors COLORSCHEME -function colorscheme_ansi_colors() { - unpack_colorscheme_field "$1" 4 +# profile_get_font PROFILE +# Echoes the font for the given gnome-terminal profile. +function profile_get_font() { + PROFILE="$1" + profile_get_property "${PROFILE}" "font" } -GRUVBOX8_COLORSCHEME=$(colorscheme "gruvbox8" \ - "#ebdbb2" \ - "#1d2021" \ - $(ansi_colors \ - "#1d2021" \ - "#cc241d" \ - "#98971a" \ - "#d79921" \ - "#458588" \ - "#b16286" \ - "#689d6a" \ - "#a89984" \ - "#928374" \ - "#fb4934" \ - "#b8bb26" \ - "#fabd2f" \ - "#83a598" \ - "#d3869b" \ - "#8ec07c" \ - "#ebdbb2" - ) -) +# profile_set_font PROFILE VALUE +# Sets the font for the given gnome-terminal profile. +function profile_set_font() { + PROFILE="$1" + VALUE="$2" + profile_set_property "${PROFILE}" "font" "${VALUE}" +} -COLORSCHEMES=( - "${GRUVBOX8_COLORSCHEME}" -) +# profile_get_foreground_color PROFILE +# Echoes the foreground color for the given gnome-terminal profile. +function profile_get_foreground_color() { + PROFILE="$1" + profile_get_property "${PROFILE}" "foreground-color" +} -# log ARGS... -# echo for stderr -function log() { - echo 1>&2 "$@" +# profile_set_foreground_color PROFILE VALUE +# Sets the foreground color for the given gnome-terminal profile. +function profile_set_foreground_color() { + PROFILE="$1" + VALUE="$2" + profile_set_property "${PROFILE}" "foreground-color" "${VALUE}" } -# die ARGS... -# log then exit with an error code -function die() { - log "$@" - exit 1 +# profile_get_background_color PROFILE +# Echoes the background color for the given gnome-terminal profile. +function profile_get_background_color() { + PROFILE="$1" + profile_get_property "${PROFILE}" "background_color" } -function get_profile_property() { +# profile_set_background_color PROFILE VALUE +# Sets the background color for the given gnome-terminal profile. +function profile_set_background_color() { PROFILE="$1" - PROPERTY="$2" + VALUE="$2" + profile_set_property "${PROFILE}" "background-color" "${VALUE}" +} - dconf read "${PROFILE}${PROPERTY}" +# profile_get_ansi_colors PROFILE +# Echoes the ANSI color palette for the given gnome-terminal profile. +function profile_get_ansi_colors() { + PROFILE="$1" + profile_get_property "${PROFILE}" "palette" } -# get_profile_name PROFILE -# Echoes the human-readable name for a gnome-terminal profile subdirectory. -function get_profile_name() { - get_profile_property "$1" "visible-name" +# profile_set_ansi_colors PROFILE VALUE +# Sets the ANSI color palette for the given gnome-terminal profile. +function profile_set_ansi_colors() { + PROFILE="$1" + VALUE="$2" + profile_set_property "${PROFILE}" "palette" "${VALUE}" +} + +# profile_apply_config PROFILE CONFIG_FILE +# Applies the given configuration to the given gnome-terminal profile. +function profile_apply_config() { + PROFILE="$1" + CONFIG_FILE="$2" + + FONT=$(config_get_font "${CONFIG_FILE}") + FOREGROUND_COLOR=$(config_get_foreground_color "${CONFIG_FILE}") + BACKGROUND_COLOR=$(config_get_background_color "${CONFIG_FILE}") + ANSI_COLORS=$(config_get_ansi_colors "${CONFIG_FILE}") + + profile_set_font "${PROFILE}" "${FONT}" + profile_set_foreground_color "${PROFILE}" "${FOREGROUND_COLOR}" + profile_set_background_color "${PROFILE}" "${BACKGROUND_COLOR}" + profile_set_ansi_colors "${PROFILE}" "${ANSI_COLORS}" } +# INTERACTIVE USE +# =============== + # choose_number BASE_PROMPT MIN MAX # Asks the user to choose a number between MIN and MAX, inclusive. # BASE_PROMPT should not end in a newline. @@ -110,6 +250,7 @@ function choose_number() { PROMPT="$1" MIN_NUMBER="$2" MAX_NUMBER="$3" + while true; do read -p "$1 [${MIN_NUMBER} to ${MAX_NUMBER}]: " CHOSEN_NUMBER if [ "${CHOSEN_NUMBER}" -lt "${MIN_NUMBER}" ]; then @@ -133,81 +274,42 @@ function choose_profile() { PROFILES+=("${ROOT_DCONF_DIR}${PROFILE_SUBDIR}") done NUM_PROFILES="${#PROFILES[@]}" - + if [ "${NUM_PROFILES}" -eq 0 ]; then die "Error: found no gnome-terminal profiles to style." fi if [ "${NUM_PROFILES}" -eq 1 ]; then PROFILE="${PROFILES[0]}" - PROFILE_NAME=$(get_profile_name "${PROFILE}") + PROFILE_NAME=$(profile_get_name "${PROFILE}") log "Found single gnome-terminal profile ${PROFILE_NAME}, using it." echo "${PROFILE}" return fi log "Available gnome-terminal profiles:" - + for i in $(seq 1 "${NUM_PROFILES}"); do PROFILE="${PROFILES[$(($i - 1))]}" - PROFILE_NAME=$(get_profile_name "${PROFILE}") + PROFILE_NAME=$(profile_get_name "${PROFILE}") log " #$i: ${PROFILE_NAME}" done choose_number "Choose a profile" 1 "${NUM_PROFILES}" } -# find_colorscheme COLORSCHEME_NAME -# Echoes the colorscheme if it exists. -# Dies otherwise with a useful message. -function find_colorscheme() { - COLORSCHEME_NAME="$1" - - NAMES=() - for COLORSCHEME in "${COLORSCHEMES[@]}"; do - NAME=$(colorscheme_name ${COLORSCHEME}) - NAMES+=("${NAME}") - - if [ "${NAME,,}" == "${NAME,,}" ]; then - echo "${COLORSCHEME}" - return - fi - done - - log "Error: no such colorscheme '${COLORSCHEME_NAME}'. Options are:" - for NAME in "${NAMES[@]}"; do - log " - ${NAME}" - done - exit 1 -} - -# apply_colorscheme PROFILE COLORSCHEME -# Applies the given packed colorscheme to the given gnome-terminal profile. -function apply_colorscheme() { - PROFILE="$1" - COLORSCHEME="$2" - - FG_COLOR=$(colorscheme_fg_color "${COLORSCHEME}") - BG_COLOR=$(colorscheme_bg_color "${COLORSCHEME}") - ANSI_COLORS=$(colorscheme_ansi_colors "${COLORSCHEME}") - - dconf write "${PROFILE}foreground-color" "'${FG_COLOR}'" - dconf write "${PROFILE}background-color" "'${BG_COLOR}'" - dconf write "${PROFILE}palette" "${ANSI_COLORS}" -} - function main() { if [ "$#" -le 0 ]; then - die "USAGE: style-gnome-terminal COLORSCHEME_NAME" + die "USAGE: style-gnome-terminal CONFIG_FILE" fi # TODO: Subcommands: - # + # # Profiles: - # + # # - profile list # - profile show NAME_OR_ID - # + # # Styling: # # These all take an optional [profile NAME_OR_ID] clause to specify a profile @@ -233,10 +335,9 @@ function main() { # arbitrary code (rules our source-ing .sh files containing variables), that # does not require onerous dependencies to parse. - COLORSCHEME_NAME="$1" - COLORSCHEME=$(find_colorscheme "${COLORSCHEME_NAME}") + CONFIG_FILE="$1" PROFILE=$(choose_profile) - apply_colorscheme "${PROFILE}" "${COLORSCHEME}" + profile_apply_config "${PROFILE}" "${CONFIG_FILE}" } main "$@" diff --git a/styles/gruvbox8.conf b/styles/gruvbox8.conf new file mode 100644 index 0000000..4748a67 --- /dev/null +++ b/styles/gruvbox8.conf @@ -0,0 +1,21 @@ +font = "Hack Regular 12" + +foreground-color = "#ebdbb2" +background-color = "#1d2021" + +ansi-colors-black = "#1d2021" +ansi-colors-red = "#cc241d" +ansi-colors-green = "#98971a" +ansi-colors-yellow = "#d79921" +ansi-colors-blue = "#458588" +ansi-colors-purple = "#b16286" +ansi-colors-cyan = "#689d6a" +ansi-colors-white = "#a89984" +ansi-colors-bright-black = "#928374" +ansi-colors-bright-red = "#fb4934" +ansi-colors-bright-green = "#b8bb26" +ansi-colors-bright-yellow = "#fabd2f" +ansi-colors-bright-blue = "#83a598" +ansi-colors-bright-purple = "#d3869b" +ansi-colors-bright-cyan = "#8ec07c" +ansi-colors-bright-white = "#ebdbb2"