Last active
July 11, 2019 02:31
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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