Skip to content

Instantly share code, notes, and snippets.

@jlegrone
Last active July 11, 2019 02:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jlegrone/8ee5986dd219c1ea0da1abbe015b2c23 to your computer and use it in GitHub Desktop.
Save jlegrone/8ee5986dd219c1ea0da1abbe015b2c23 to your computer and use it in GitHub Desktop.
An interactive touchbar plugin for zsh that supports git, kubernetes, and npm scripts.
# Based on https://github.com/iam4x/zsh-iterm-touchbar
GIT_UNTRACKED="${GIT_UNTRACKED:-🧹}"
GIT_STASHED="${GIT_STASHED:-🥡}"
GIT_UNSTAGED="${GIT_UNSTAGED:-🥚}"
GIT_UNCOMMITTED="${GIT_UNCOMMITTED:-🐥}"
GIT_UNPUSHED="${GIT_UNPUSHED:-🐓}"
GIT_UNPULLED="${GIT_UNPULLED:-⇣}"
GIT_UP_TO_DATE="${GIT_UP_TO_DATE:-🙌}"
K8S_CLUSTER="${K8S_CLUSTER:-🌊}"
K8S_NAMESPACE="${K8S_NAMESPACE:-🛳️}"
YARN_SCRIPTS="${YARN_SCRIPTS:-🧶 scripts}"
NPM_SCRIPTS="${NPM_SCRIPTS:-⚡️ scripts}"
PREVIOUS_MENU="${PREVIOUS_MENU:-👈 back}"
YARN_ENABLED=true
TOUCHBAR_GIT_ENABLED=true
TOUCHBAR_K8S_ENABLED=true
current_namespace() {
local cur_ctx
cur_ctx="$(current_context)" || exit_err "error getting current context"
ns="$(kubectl config view -o=jsonpath="{.contexts[?(@.name==\"${cur_ctx}\")].context.namespace}")" \
|| exit_err "error getting current namespace"
if [[ -z "${ns}" ]]; then
echo "default"
else
echo "${ns}"
fi
}
current_context() {
kubectl config current-context
}
get_namespaces() {
kubectl get namespaces -o=jsonpath='{range .items[*].metadata.name}{@}{"\n"}{end}'
}
# https://unix.stackexchange.com/a/22215
find-up () {
path=$PWD
while [[ "$path" != "" && ! -e "$path/$1" ]]; do
path=${path%/*}
done
echo "$path"
}
# Output name of current branch.
git_current_branch() {
local ref
ref=$(command git symbolic-ref --quiet HEAD 2> /dev/null)
local ret=$?
if [[ $ret != 0 ]]; then
[[ $ret == 128 ]] && return # no git repo.
ref=$(command git rev-parse --short HEAD 2> /dev/null) || return
fi
echo ${ref#refs/heads/}
}
# Uncommitted changes.
# Check for uncommitted changes in the index.
git_uncomitted() {
if ! $(git diff --quiet --ignore-submodules --cached); then
echo -n "${GIT_UNCOMMITTED}"
fi
}
# Unstaged changes.
# Check for unstaged changes.
git_unstaged() {
if ! $(git diff-files --quiet --ignore-submodules --); then
echo -n "${GIT_UNSTAGED}"
fi
}
# Untracked files.
# Check for untracked files.
git_untracked() {
if [ -n "$(git ls-files --others --exclude-standard)" ]; then
echo -n "${GIT_UNTRACKED}"
fi
}
# Stashed changes.
# Check for stashed changes.
git_stashed() {
if $(git rev-parse --verify refs/stash &>/dev/null); then
echo -n "${GIT_STASHED}"
fi
}
# Unpushed and unpulled commits.
# Get unpushed and unpulled commits from remote and draw arrows.
git_unpushed_unpulled() {
# check if there is an upstream configured for this branch
command git rev-parse --abbrev-ref @'{u}' &>/dev/null || return
local count
count="$(command git rev-list --left-right --count HEAD...@'{u}' 2>/dev/null)"
# exit if the command failed
(( !$? )) || return
# counters are tab-separated, split on tab and store as array
count=(${(ps:\t:)count})
local arrows left=${count[1]} right=${count[2]}
(( ${left:-0} > 0 )) && arrows+="${GIT_UNPUSHED}"
(( ${right:-0} > 0 )) && arrows+="${GIT_UNPULLED}"
[ -n $arrows ] && echo -n "${arrows}"
}
pecho() {
if [ -n "$TMUX" ]; then
echo -ne "\ePtmux;\e$*\e\\"
else
echo -ne $*
fi
}
# F1-12: https://github.com/vmalloc/zsh-config/blob/master/extras/function_keys.zsh
# F13-F20: just running read and pressing F13 through F20. F21-24 don't print escape sequences
fnKeys=('^[OP' '^[OQ' '^[OR' '^[OS' '^[[15~' '^[[17~' '^[[18~' '^[[19~' '^[[20~' '^[[21~' '^[[23~' '^[[24~' '^[[1;2P' '^[[1;2Q' '^[[1;2R' '^[[1;2S' '^[[15;2~' '^[[17;2~' '^[[18;2~' '^[[19;2~')
touchBarState=''
npmScripts=()
gitBranches=()
lastPackageJsonPath=''
function _clearTouchbar() {
pecho "\033]1337;PopKeyLabels\a"
}
function _unbindTouchbar() {
for fnKey in "$fnKeys[@]"; do
bindkey -s "$fnKey" ''
done
}
function setKey(){
pecho "\033]1337;SetKeyLabel=F${1}=${2}\a"
if [ "$4" != "-q" ]; then
bindkey -s $fnKeys[$1] "$3 \n"
else
bindkey $fnKeys[$1] $3
fi
}
function clearKey(){
pecho "\033]1337;SetKeyLabel=F${1}=F${1}\a"
}
function _displayDefault() {
if [[ $touchBarState != "" ]]; then
_clearTouchbar
fi
_unbindTouchbar
touchBarState=""
# CURRENT_DIR
# -----------
setKey 1 "$(echo $PWD | awk -F/ '{print $(NF-1)"/"$(NF)}')" _displayPath '-q'
# GIT
# ---
# Check if the current directory is a git repository and not the .git directory
let offset=1
if [[ "$TOUCHBAR_GIT_ENABLED" = true ]] &&
git rev-parse --is-inside-work-tree &>/dev/null &&
[[ "$(git rev-parse --is-inside-git-dir 2> /dev/null)" == 'false' ]]; then
# Ensure the index is up to date.
git update-index --really-refresh -q &>/dev/null
# String of indicators
local indicators=''
indicators+="$(git_untracked)"
indicators+="$(git_stashed)"
indicators+="$(git_unstaged)"
indicators+="$(git_uncomitted)"
indicators+="$(git_unpushed_unpulled)"
[ -n "${indicators}" ] && touchbarIndicators="${indicators}" || touchbarIndicators="$GIT_UP_TO_DATE";
setKey $((offset + 1)) "🌳 `git_current_branch`" _displayBranches '-q'
setKey $((offset + 2)) $touchbarIndicators "git status"
offset=$offset+2
fi
let offset=$offset
if [ "$TOUCHBAR_K8S_ENABLED" = true ]; then
setKey $((offset + 1)) "$K8S_CLUSTER $(kubectl config current-context)" _displayClusters '-q'
setKey $((offset + 2)) "$K8S_NAMESPACE $(current_namespace)" kubens
offset=$offset+2
fi
# PACKAGE.JSON
# ------------
let offset=$offset
if [[ $(find-up package.json) != "" ]]; then
if [[ $(find-up yarn.lock) != "" ]] && [[ "$YARN_ENABLED" = true ]]; then
setKey $((offset + 1)) "$YARN_SCRIPTS" _displayYarnScripts '-q'
else
setKey $((offset + 1)) "$NPM_SCRIPTS" _displayNpmScripts '-q'
fi
offset=$offset+1
fi
let total_offset=$offset+1
for i in {$total_offset..24}
do
clearKey $i
done
}
function _displayNpmScripts() {
# find available npm run scripts only if new directory
if [[ $lastPackageJsonPath != $(find-up package.json) ]]; then
lastPackageJsonPath=$(find-up package.json)
npmScripts=($(node -e "console.log(Object.keys($(npm run --json)).sort((a, b) => a.localeCompare(b)).filter((name, idx) => idx < 19).join(' '))"))
fi
_clearTouchbar
_unbindTouchbar
touchBarState='npm'
fnKeysIndex=1
for npmScript in "$npmScripts[@]"; do
fnKeysIndex=$((fnKeysIndex + 1))
setKey $fnKeysIndex $npmScript "npm run $npmScript"
done
setKey 1 "$PREVIOUS_MENU" _displayDefault '-q'
}
function _displayYarnScripts() {
# find available yarn run scripts only if new directory
if [[ $lastPackageJsonPath != $(find-up package.json) ]]; then
lastPackageJsonPath=$(find-up package.json)
yarnScripts=($(node -e "console.log([$(yarn run --json 2>>/dev/null | tr '\n' ',')].find(line => line && line.type === 'list' && line.data && line.data.type === 'possibleCommands').data.items.sort((a, b) => a.localeCompare(b)).filter((name, idx) => idx < 19).join(' '))"))
fi
_clearTouchbar
_unbindTouchbar
touchBarState='yarn'
fnKeysIndex=1
for yarnScript in "$yarnScripts[@]"; do
fnKeysIndex=$((fnKeysIndex + 1))
setKey $fnKeysIndex $yarnScript "yarn run $yarnScript"
done
setKey 1 "$PREVIOUS_MENU" _displayDefault '-q'
}
function _displayBranches() {
# List of branches for current repo
gitBranches=($(node -e "console.log('$(echo $(git branch))'.split(/[ ,]+/).toString().split(',').join(' ').toString().replace('* ', ''))"))
_clearTouchbar
_unbindTouchbar
# change to github state
touchBarState='github'
fnKeysIndex=1
# for each branch name, bind it to a key
for branch in "$gitBranches[@]"; do
fnKeysIndex=$((fnKeysIndex + 1))
setKey $fnKeysIndex $branch "git checkout $branch"
done
setKey 1 "$PREVIOUS_MENU" _displayDefault '-q'
}
function _displayClusters() {
# List of branches for current repo
clusters=($(node -e "console.log('$(echo $(kubectx))'.split(/[ ,]+/).toString().split(',').join(' ').toString().replace('* ', ''))"))
_clearTouchbar
_unbindTouchbar
# change to github state
touchBarState='kubernetes-clusters'
fnKeysIndex=1
# for each branch name, bind it to a key
for cluster in "$clusters[@]"; do
fnKeysIndex=$((fnKeysIndex + 1))
setKey $fnKeysIndex $cluster "kubectx $cluster"
done
setKey 1 "$PREVIOUS_MENU" _displayDefault '-q'
}
function _displayNamespaces() {
namespaces=($(node -e "console.log('$(echo $(get_namespaces))'.split(/[ ,]+/).toString().split(',').join(' ').toString().replace('* ', ''))"))
_clearTouchbar
_unbindTouchbar
touchBarState='kubernetes-namespaces'
fnKeysIndex=1
for ns in "$namespaces[@]"; do
fnKeysIndex=$((fnKeysIndex + 1))
setKey $fnKeysIndex $cluster "kubens $ns"
done
setKey 1 "$PREVIOUS_MENU" _displayDefault '-q'
}
function _displayPath() {
_clearTouchbar
_unbindTouchbar
touchBarState='path'
IFS="/" read -rA directories <<< "$PWD"
fnKeysIndex=2
for dir in "${directories[@]:1}"; do
setKey $fnKeysIndex "$dir" "cd $(pwd | cut -d'/' -f-$fnKeysIndex)"
fnKeysIndex=$((fnKeysIndex + 1))
done
setKey 1 "$PREVIOUS_MENU" _displayDefault '-q'
}
zle -N _displayDefault
zle -N _displayNpmScripts
zle -N _displayYarnScripts
zle -N _displayBranches
zle -N _displayClusters
zle -N _displayPath
precmd_iterm_touchbar() {
if [[ $touchBarState == 'npm' ]]; then
_displayNpmScripts
elif [[ $touchBarState == 'yarn' ]]; then
_displayYarnScripts
elif [[ $touchBarState == 'kubernetes-clusters' ]]; then
_displayClusters
elif [[ $touchBarState == 'kubernetes-namespaces' ]]; then
_displayNamespaces
elif [[ $touchBarState == 'github' ]]; then
_displayBranches
elif [[ $touchBarState == 'path' ]]; then
_displayPath
else
_displayDefault
fi
}
autoload -Uz add-zsh-hook
add-zsh-hook precmd precmd_iterm_touchbar
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment