mirror of
https://github.com/ohmyzsh/ohmyzsh.git
synced 2026-05-22 04:51:12 +02:00
Rewrite theme() and lstheme() to remove side effects, add debugging, and fix `local` inside theme definitions. Add `theme next` for stepping through theme list, and "blacklist" support. Modify tools/theme_chooser to use new theme functions.
418 lines
14 KiB
Bash
418 lines
14 KiB
Bash
# Configure and enable ls colors
|
|
|
|
autoload -U colors && colors
|
|
|
|
export LSCOLORS="Gxfxcxdxbxegedabagacad"
|
|
|
|
# TODO organise this chaotic logic
|
|
|
|
if [[ $DISABLE_LS_COLORS != "true" ]]; then
|
|
# Find the option for using colors in ls, depending on the version: Linux or BSD
|
|
if [[ "$OSTYPE" == netbsd* ]]; then
|
|
# On NetBSD, test if "gls" (GNU ls) is installed (this one supports colors);
|
|
# otherwise, leave ls as is, because NetBSD's ls doesn't support -G
|
|
gls --color -d . &>/dev/null && alias ls='gls --color=tty'
|
|
elif [[ "$OSTYPE" == openbsd* ]]; then
|
|
# On OpenBSD, "gls" (ls from GNU coreutils) and "colorls" (ls from base,
|
|
# with color and multibyte support) are available from ports. "colorls"
|
|
# will be installed on purpose and can't be pulled in by installing
|
|
# coreutils, so prefer it to "gls".
|
|
gls --color -d . &>/dev/null && alias ls='gls --color=tty'
|
|
colorls -G -d . &>/dev/null && alias ls='colorls -G'
|
|
elif [[ "$OSTYPE" == darwin* ]]; then
|
|
# this is a good alias, it works by default just using $LSCOLORS
|
|
ls -G . &>/dev/null && alias ls='ls -G'
|
|
|
|
# only use coreutils ls if there is a dircolors customization present ($LS_COLORS or .dircolors file)
|
|
# otherwise, gls will use the default color scheme which is ugly af
|
|
[[ -n "$LS_COLORS" || -f "$HOME/.dircolors" ]] && gls --color -d . &>/dev/null && alias ls='gls --color=tty'
|
|
else
|
|
# For GNU ls, we use the default ls color theme. They can later be overwritten by themes.
|
|
if [[ -z "$LS_COLORS" ]]; then
|
|
(( $+commands[dircolors] )) && eval "$(dircolors -b)"
|
|
fi
|
|
|
|
ls --color -d . &>/dev/null && alias ls='ls --color=tty' || { ls -G . &>/dev/null && alias ls='ls -G' }
|
|
|
|
# Take advantage of $LS_COLORS for completion as well.
|
|
zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
|
|
fi
|
|
fi
|
|
|
|
if [[ -n $WINDOW ]]; then
|
|
SCREEN_NO="%B$WINDOW%b "
|
|
else
|
|
SCREEN_NO=""
|
|
fi
|
|
|
|
# OMZ themes use promptsubst, so make sure it's on
|
|
setopt prompt_subst
|
|
|
|
# Set to true to enable debugging output for theme functions
|
|
# Note that this can cause unstable behavior, especially with these themes:
|
|
# agnoster dstufft
|
|
ZSH_THEME_DEBUG=${ZSH_THEME_DEBUG:-false}
|
|
# These themes are known to interact badly with our debugging support
|
|
_OMZ_DEBUG_BLACKLISTED_THEMES=(agnoster dstufft dogenpunk fino fino-time half-life \
|
|
kolo peepcode smt steeef suvash zhann)
|
|
|
|
|
|
# Loads a theme
|
|
#
|
|
# Usage:
|
|
# theme <name> - load named theme
|
|
# theme random - load a random theme
|
|
# theme <n> - load a theme by index (where <n> is an integer)
|
|
# theme next - load the next theme in the list of defined themes
|
|
#
|
|
# If no argument is given, `theme random` is the default behavior.
|
|
#
|
|
# Return value:
|
|
# 0 on success
|
|
# Nonzero on failure, such as requested theme not being found or theme index
|
|
# out of range.
|
|
#
|
|
# The "random" and "next" forms will skip over themes in the ZSH_BLACKLISTED_THEMES
|
|
# variable.
|
|
#
|
|
# As a consequence of this argument syntax, themes may not be named "random", "next",
|
|
# or an integer number.
|
|
function theme() {
|
|
local themes n_themes random_theme blacklist
|
|
if [[ -z $1 || $1 == "random" ]]; then
|
|
# Special case: random theme
|
|
blacklist=($(_omz_theme_blacklist))
|
|
themes=($(lstheme))
|
|
#themes=(${themes:|blacklist})
|
|
_omz_array_setdiff themes themes blacklist
|
|
n_themes=${#themes[@]}
|
|
((n=(RANDOM%n_themes)+1))
|
|
random_theme=${themes[$n]}
|
|
echo "[oh-my-zsh] Loading random theme '$random_theme'..."
|
|
theme $random_theme
|
|
elif [[ $1 == "next" ]]; then
|
|
# Auto-advance to next theme
|
|
_omz_theme_n next
|
|
elif [[ $1 =~ '^[0-9]+$' ]]; then
|
|
# Select theme by index
|
|
_omz_theme_n $1
|
|
else
|
|
# Main case: load named theme
|
|
_omz_load_theme $1
|
|
fi
|
|
}
|
|
|
|
function _omz_load_theme() {
|
|
local name=$1
|
|
local theme_dir found
|
|
found=false
|
|
for theme_dir ($ZSH_CUSTOM $ZSH_CUSTOM/themes $ZSH/themes); do
|
|
if [[ -f "$theme_dir/$name.zsh-theme" ]]; then
|
|
found=true
|
|
_omz_load_theme_from_file $name "$theme_dir/$name.zsh-theme"
|
|
break
|
|
fi
|
|
done
|
|
if [[ $found == false ]]; then
|
|
echo "[oh-my-zsh] Theme not found: '$name'"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Loads a theme by index
|
|
#
|
|
# Usage:
|
|
# themen - advance to the next theme
|
|
# themen next - advance to the next theme
|
|
# themen <n> - load theme number <n>
|
|
#
|
|
# Loads a theme selected by index into list of defined themes, instead of by
|
|
# name. This is to make it easy to cycle through all the themes for debugging
|
|
# or browsing purposes.
|
|
#
|
|
# If called without an argument, it just advances to the next theme in the list
|
|
#
|
|
# `themen next` will ignore themes in the ZSH_BLACKLISTED_THEMES variable.
|
|
function _omz_theme_n() {
|
|
# Numeric index argument: select theme by index
|
|
local themes n name n_themes blacklist
|
|
themes=($(lstheme))
|
|
n_themes=${#themes[@]}
|
|
if [[ -z $1 || $1 == "next" ]]; then
|
|
# Auto-advance to next theme
|
|
# Find index of currently loaded theme
|
|
local last_n=${themes[(i)$ZSH_THEME]}
|
|
(( n = (last_n % n_themes) + 1 ))
|
|
# Advance past blacklisted themes. Do it this way instead of removing blacklist
|
|
# from themes list to keep indexes stable
|
|
blacklist=($(_omz_theme_blacklist))
|
|
while [[ ${blacklist[(i)${themes[n]}]} -le ${#blacklist} ]]; do
|
|
(( n = n % n_themes + 1 ))
|
|
done
|
|
else
|
|
n=$1
|
|
if [[ $n -gt $n_themes ]]; then
|
|
printf "[oh-my-zsh]: Theme index %s is out of range (max is %s)\n" $n $n_themes
|
|
return 1
|
|
fi
|
|
fi
|
|
name=${themes[$n]}
|
|
echo "Loading theme #$n/$n_themes: '$name'"
|
|
theme $name
|
|
}
|
|
|
|
# Computes the set difference between two arrays using only ZSH 4.x features
|
|
#
|
|
# _omz_array_setdiff(c, a, b)
|
|
#
|
|
# Determines all the items in $a that are not in $b, and outputs them.
|
|
# This is the same functionality as ZSH 5.x's "c=(${a:|b})" expansion operation,
|
|
# but is re-implemented here for compatibility with ZSH 4.x.
|
|
#
|
|
# This operates by "reference", so caller should pass in names of the input
|
|
# parameters, not their values.
|
|
#
|
|
# Implementation note: the input variables may not be named "omz_asd_a", "omz_asd_b",
|
|
# or "omz_asd_out_name".
|
|
# due to dynamic scoping conflicts. And you cannot use the numeric parameters ($1, $2, etc)
|
|
# as inputs.
|
|
#
|
|
# Example:
|
|
#
|
|
# a=(foo bar baz)
|
|
# b=(bar qux)
|
|
# _omz_array_setdiff c a b
|
|
#
|
|
# After the function call, $c will contain (foo baz).
|
|
#
|
|
function _omz_array_setdiff() {
|
|
local omz_asd_a omz_asd_b omz_asd_out_name
|
|
omz_asd_out_name=$1
|
|
omz_asd_a=(${(P)2})
|
|
omz_asd_b=(${(P)3})
|
|
local my_out item
|
|
my_out=()
|
|
for item (${omz_asd_a}); do
|
|
if [[ ${omz_asd_b[(i)$item]} -gt ${#omz_asd_b} ]]; then
|
|
my_out+=($item)
|
|
fi
|
|
done
|
|
set -A $omz_asd_out_name $my_out
|
|
}
|
|
|
|
|
|
# Variables to track hook functions installed by themes
|
|
_OMZ_THEME_CHPWD_FUNCTIONS=()
|
|
_OMZ_THEME_PRECMD_FUNCTIONS=()
|
|
_OMZ_THEME_PREEXEC_FUNCTIONS=()
|
|
|
|
# Outputs the effective theme blacklist
|
|
function _omz_theme_blacklist() {
|
|
local blacklist=$ZSH_BLACKLISTED_THEMES
|
|
if [[ $ZSH_THEME_DEBUG == true ]]; then
|
|
blacklist+=($_OMZ_DEBUG_BLACKLISTED_THEMES)
|
|
fi
|
|
echo ${(F)blacklist}
|
|
}
|
|
|
|
# Implementation of loading a theme
|
|
# Most of the code in here is for tracking hooks and debugging support
|
|
function _omz_load_theme_from_file() {
|
|
local name=$1 file=$2 params_before exclude_params
|
|
local -A values_before
|
|
|
|
# Reset so theme is loaded into a clean slate
|
|
_omz_reset_theme
|
|
|
|
# Set up tracking and debugging
|
|
local chpwd_fcns_0 precmd_fcns_0 preexec_fcns_0 chpwd_0 precmd_0 preexec_0
|
|
local params_after params_added param ignore_params
|
|
chpwd_0=$(which chpwd)
|
|
precmd_0=$(which precmd)
|
|
preexec_0=$(which preexec)
|
|
chpwd_fcns_0=($chpwd_functions)
|
|
precmd_fcns_0=($precmd_functions)
|
|
preexec_fcns_0=($preexec_functions)
|
|
if [[ $ZSH_THEME_DEBUG == true ]]; then
|
|
params_before=($(set +))
|
|
fi
|
|
for param ($params_before); do
|
|
values_before[$param]=${(P)param}
|
|
done
|
|
|
|
# Actually load the theme, using an indirection function
|
|
_omz_source_theme_file $file
|
|
|
|
# Debugging stuff
|
|
if [[ $ZSH_THEME_DEBUG == true ]]; then
|
|
params_after=($(set +))
|
|
#params_added=(${params_after:|params_before})
|
|
_omz_array_setdiff params_added params_after params_before
|
|
ignore_params=(LINENO RANDOM _ parameters prompt values_before modules \
|
|
params_added ignore_params params_before params_after params_changed \
|
|
SECONDS TTYIDLE PS1 PS2 PS3 PS4 RPS1 RPS2)
|
|
#params_before=(${params_before:|ignore_params})
|
|
_omz_array_setdiff params_before params_before ignore_params
|
|
local params_changed
|
|
params_changed=()
|
|
for param ($params_before); do
|
|
if [[ "${(P)param}" != "${values_before[$param]}" ]]; then
|
|
params_changed+=($param)
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Record which theme was loaded
|
|
ZSH_THEME=$name
|
|
|
|
# Track changes to hooks
|
|
#_OMZ_THEME_CHPWD_FUNCTIONS=(${chpwd_functions:|chpwd_fcns_0})
|
|
_omz_array_setdiff _OMZ_THEME_CHPWD_FUNCTIONS chpwd_functions chpwd_fcns_0
|
|
#_OMZ_THEME_PRECMD_FUNCTIONS=(${precmd_functions:|precmd_fcns_0})
|
|
_omz_array_setdiff _OMZ_THEME_PRECMD_FUNCTIONS precmd_functions precmd_fcns_0
|
|
#_OMZ_THEME_PREEXEC_FUNCTIONS=(${preexec_functions:|preexec_fcns_0})
|
|
_omz_array_setdiff _OMZ_THEME_PREEXEC_FUNCTIONS preexec_functions preexec_fcns_0
|
|
|
|
# Post-loading debugging
|
|
if [[ $ZSH_THEME_DEBUG == true ]]; then
|
|
echo "Loaded theme '$name' from file $file"
|
|
if [[ -n $params_added ]]; then
|
|
printf '=== %s ===\n%s\n' "Theme added parameters:" ${(F)params_added}
|
|
fi
|
|
if [[ -n $params_changed ]]; then
|
|
printf '=== %s ===\n%s\n' "Theme changed parameters:" ${(F)params_changed}
|
|
fi
|
|
if [[ -n "${_OMZ_THEME_CHPWD_FUNCTIONS}${_OMZ_THEME_PRECMD_FUNCTIONS}${_OMZ_THEME_PREEXEC_FUNCTIONS}" ]]; then
|
|
printf '=== %s ===\n' "Theme added hooks:"
|
|
if [[ -n $_OMZ_THEME_CHPWD_FUNCTIONS ]]; then
|
|
echo "Theme added chpwd hooks: $_OMZ_THEME_CHPWD_FUNCTIONS"
|
|
fi
|
|
if [[ -n $_OMZ_THEME_PRECMD_FUNCTIONS ]]; then
|
|
echo "Theme added precmd hooks: $_OMZ_THEME_PRECMD_FUNCTIONS"
|
|
fi
|
|
if [[ -n $_OMZ_THEME_PREEXEC_FUNCTIONS ]]; then
|
|
echo "Theme added preexec hooks: $_OMZ_THEME_PREEXEC_FUNCTIONS"
|
|
fi
|
|
fi
|
|
if [[ $(which chpwd) != $chpwd_0 ]]; then
|
|
echo "WARNING: Theme changed chpwd()"
|
|
fi
|
|
if [[ $(which preexec) != $preexec_0 ]]; then
|
|
echo "WARNING: Theme changed preexec()"
|
|
fi
|
|
if [[ $(which precmd) != $precmd_0 ]]; then
|
|
echo "WARNING: Theme changed precmd()"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Sources the given file
|
|
# The only reason this function exists is to provide a layer of
|
|
# indirection so that the theme file runs in its own function call stack frame
|
|
# and "local" statements in the theme definitions work as intended.
|
|
function _omz_source_theme_file() {
|
|
source $1
|
|
}
|
|
|
|
# Resets all theme settings to their default state
|
|
# (To the extent that we know what themes do, that is.)
|
|
# This will reset all variables used by the core OMZ *_prompt_info functions.
|
|
# It will also remove any hook functions installed by the current theme, if it
|
|
# was loaded by the theme() function
|
|
function _omz_reset_theme() {
|
|
# Prompts
|
|
PROMPT="%n@%m:%~%# "
|
|
PROMPT2='%_> '
|
|
PROMPT3='?# '
|
|
PROMPT4='+%N:%i> '
|
|
unset RPROMPT RPROMPT2
|
|
|
|
# This assumes that all ZSH_THEME_<aspect>_* variables are owned by
|
|
# OMZ theming, and can be reset en masse
|
|
# All the commented-out variables are there to serve as a list of things
|
|
# that are used by theme support and could be given default values
|
|
|
|
# git_prompt_info variables
|
|
unset -m 'ZSH_THEME_GIT_PROMPT_*'
|
|
ZSH_THEME_GIT_PROMPT_PREFIX="git:(" # Prefix at the very beginning of the prompt, before the branch name
|
|
ZSH_THEME_GIT_PROMPT_SUFFIX=")" # At the very end of the prompt
|
|
#ZSH_THEME_GIT_PROMPT_PREFIX=
|
|
#ZSH_THEME_GIT_PROMPT_SUFFIX=
|
|
#ZSH_THEME_GIT_COMMITS_AHEAD_PREFIX=
|
|
#ZSH_THEME_GIT_COMMITS_AHEAD_SUFFIX=
|
|
ZSH_THEME_GIT_PROMPT_DIRTY="*" # Text to display if the branch is dirty
|
|
ZSH_THEME_GIT_PROMPT_CLEAN="" # Text to display if the branch is clean
|
|
#ZSH_THEME_GIT_PROMPT_BEHIND_REMOTE=
|
|
#ZSH_THEME_GIT_PROMPT_AHEAD_REMOTE=
|
|
#ZSH_THEME_GIT_PROMPT_DIVERGED_REMOTE=
|
|
#ZSH_THEME_GIT_PROMPT_AHEAD=
|
|
#ZSH_THEME_GIT_PROMPT_BEHIND=
|
|
#ZSH_THEME_GIT_PROMPT_SHA_BEFORE=
|
|
#ZSH_THEME_GIT_PROMPT_SHA_AFTER=
|
|
#ZSH_THEME_GIT_PROMPT_ADDED=
|
|
#ZSH_THEME_GIT_PROMPT_MODIFIED=
|
|
#ZSH_THEME_GIT_PROMPT_RENAMED=
|
|
#ZSH_THEME_GIT_PROMPT_DELETED=
|
|
#ZSH_THEME_GIT_PROMPT_STASHED=
|
|
#ZSH_THEME_GIT_PROMPT_UNMERGED=
|
|
#ZSH_THEME_GIT_PROMPT_DIVERGED=
|
|
#ZSH_THEME_GIT_PROMPT_UNTRACKED=
|
|
# nvm_prompt_info variables
|
|
unset -m 'ZSH_THEME_NVM_PROMPT_*'
|
|
#ZSH_THEME_NVM_PROMPT_PREFIX=
|
|
#ZSH_THEME_NVM_PROMPT_SUFFIX=
|
|
# rvm_prompt_info variables
|
|
unset -m 'ZSH_THEME_RVM_PROMPT_*'
|
|
#ZSH_THEME_RVM_PROMPT_PREFIX=
|
|
#ZSH_THEME_RVM_PROMPT_SUFFIX=
|
|
#ZSH_THEME_RVM_PROMPT_OPTIONS=
|
|
# svn_prompt_info variables
|
|
unset -m 'ZSH_THEME_SVN_PROMPT_*'
|
|
#ZSH_THEME_SVN_PROMPT_CLEAN
|
|
#ZSH_THEME_SVN_PROMPT_DIRTY
|
|
#ZSH_THEME_SVN_PROMPT_PREFIX
|
|
#ZSH_THEME_SVN_PROMPT_SUFFIX
|
|
|
|
# Hook functions
|
|
if [[ -n $_OMZ_THEME_CHPWD_FUNCTIONS ]]; then
|
|
#echo Removing chpwd hooks: $_OMZ_THEME_CHPWD_FUNCTIONS
|
|
#chpwd_functions=(${chpwd_functions:|_OMZ_THEME_CHPWD_FUNCTIONS})
|
|
_omz_array_setdiff chpwd_functions chpwd_functions _OMZ_THEME_CHPWD_FUNCTIONS
|
|
fi
|
|
if [[ -n $_OMZ_THEME_PRECMD_FUNCTIONS ]]; then
|
|
#echo Removing precmd hooks: $_OMZ_THEME_PRECMD_FUNCTIONS
|
|
#precmd_functions=(${precmd_functions:|_OMZ_THEME_PRECMD_FUNCTIONS})
|
|
_omz_array_setdiff precmd_functions precmd_functions _OMZ_THEME_PRECMD_FUNCTIONS
|
|
fi
|
|
if [[ -n $_OMZ_THEME_PREEXEC_FUNCTIONS ]]; then
|
|
#echo Removing preexec hooks: $_OMZ_THEME_PREEXEC_FUNCTIONS
|
|
#preexec_functions=(${preexec_functions:|_OMZ_THEME_PREEXEC_FUNCTIONS})
|
|
_omz_array_setdiff preexec_functions preexec_functions _OMZ_THEME_PREEXEC_FUNCTIONS
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
# List available themes by name
|
|
#
|
|
# The list will always be in sorted order, so you can index in to it.
|
|
# Themes in the "blacklist" are still included in lstheme, because they're
|
|
# still eligible for direct reference by explicit name or index, and that
|
|
# keeps the order stable.
|
|
function lstheme() {
|
|
local themes x theme_dir
|
|
themes=()
|
|
for theme_dir ($ZSH_CUSTOM $ZSH_CUSTOM/themes $ZSH/themes); do
|
|
if [[ -d $theme_dir ]]; then
|
|
themes+=($(_omz_lstheme_dir $theme_dir))
|
|
fi
|
|
done
|
|
themes=(${(ou)themes})
|
|
echo ${(F)themes}
|
|
}
|
|
|
|
# List themes defined in a given dir
|
|
function _omz_lstheme_dir() {
|
|
ls $1 | grep '.zsh-theme$' | sed 's,\.zsh-theme$,,'
|
|
}
|
|
|