aboutsummaryrefslogtreecommitdiff
path: root/zsh
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2016-08-27 15:54:13 +1200
committerTom Ryder <tom@sanctum.geek.nz>2016-08-27 15:54:13 +1200
commitab67c7ca6f0191d2c47f2e07731ca084559f9c4f (patch)
treedf8f50ead59fd76479ef1928059a6f4dba74888c /zsh
parentUse command -p to find tty(1) (diff)
downloaddotfiles-ab67c7ca6f0191d2c47f2e07731ca084559f9c4f.tar.gz
dotfiles-ab67c7ca6f0191d2c47f2e07731ca084559f9c4f.zip
Port Bash prompt to Zsh
Somewhat naïvely; just quick searches of `man zshall`. I'm sure some of the stuff I removed has analogous features or that they can be implemented.
Diffstat (limited to 'zsh')
-rw-r--r--zsh/zshrc7
-rw-r--r--zsh/zshrc.d/prompt.zsh214
2 files changed, 219 insertions, 2 deletions
diff --git a/zsh/zshrc b/zsh/zshrc
index 014ac3fd..2376e568 100644
--- a/zsh/zshrc
+++ b/zsh/zshrc
@@ -7,5 +7,8 @@ HISTFILE=$HOME/.zsh_history
SAVEHIST=$((1 << 12))
HISTSIZE=$((1 << 10))
-# Load POSIX shell functions
-source "$ENV"
+# Load POSIX shell startup files and then Bash-specific ones
+for sh in "$ENV" "$HOME"/.zshrc.d/*.zsh ; do
+ [[ -e $sh ]] && source "$sh"
+done
+unset -v sh
diff --git a/zsh/zshrc.d/prompt.zsh b/zsh/zshrc.d/prompt.zsh
new file mode 100644
index 00000000..a17a5e68
--- /dev/null
+++ b/zsh/zshrc.d/prompt.zsh
@@ -0,0 +1,214 @@
+# Frontend to controlling prompt
+prompt() {
+
+ # What's done next depends on the first argument to the function
+ case $1 in
+
+ # Turn complex, colored PS1 and debugging PS4 prompts on
+ on)
+ setopt promptsubst promptpercent
+
+ # Declare the PROMPT_RETURN variable as an integer
+ declare -i PROMPT_RETURN
+
+ # Set up pre-prompt command
+ precmd() {
+ PROMPT_RETURN=$?
+ }
+
+ # Basic prompt shape depends on whether we're in SSH or not
+ PS1=
+ if [[ -n $SSH_CLIENT ]] || [[ -n $SSH_CONNECTION ]] ; then
+ PS1=$PS1'%n@%m:'
+ fi
+ PS1=$PS1'%~'
+
+ # Add sub-commands; VCS, job, and return status checks
+ PS1=$PS1'$(prompt vcs)$(prompt job)$(prompt ret)'
+
+ # Add prefix and suffix
+ PS1='${PROMPT_PREFIX}'$PS1'${PROMPT_SUFFIX}'
+
+ # Add terminating "$" or "#" sign
+ PS1=$PS1'%#'
+
+ # Bold and color the prompt if it looks like we can
+ if (( $({ tput colors || tput Co ; } 2>/dev/null) >= 8 )) ; then
+ PS1='%B%F{green}'$PS1'%f%b'
+ fi
+
+ # Add a space and define the rest of the prompts
+ PS1=$PS1' '
+ PS2='> '
+ PS3='? '
+ PS4='+<$?> $LINENO:'
+ ;;
+
+ # Revert to simple inexpensive prompts
+ off)
+ unset -v precmd PROMPT_RETURN
+ PS1='\$ '
+ PS2='> '
+ PS3='? '
+ PS4='+ '
+ ;;
+
+ # Git prompt function
+ git)
+ # Bail if we're not in a work tree--or, implicitly, if we don't
+ # have git(1).
+ local iswt
+ iswt=$(git rev-parse --is-inside-work-tree 2>/dev/null)
+ [[ $iswt = true ]] || return
+
+ # Find a branch label, or a tag, or just show the short commit ID,
+ # in that order of preference; if none of that works, bail out.
+ local branch
+ branch=$( {
+ git symbolic-ref --quiet HEAD ||
+ git describe --tags --exact-match HEAD ||
+ git rev-parse --short HEAD
+ } 2>/dev/null )
+ [[ -n $branch ]] || return
+ branch=${branch##*/}
+
+ # Refresh index so e.g. git-diff-files(1) is accurate
+ git update-index --refresh >/dev/null
+
+ # Check various files in .git to flag processes
+ local proc
+ [[ -d .git/rebase-merge || -d .git/rebase-apply ]] &&
+ proc=${proc:+$proc,}'REBASE'
+ [[ -f .git/MERGE_HEAD ]] &&
+ proc=${proc:+$proc,}'MERGE'
+ [[ -f .git/CHERRY_PICK_HEAD ]] &&
+ proc=${proc:+$proc,}'PICK'
+ [[ -f .git/REVERT_HEAD ]] &&
+ proc=${proc:+$proc,}'REVERT'
+ [[ -f .git/BISECT_LOG ]] &&
+ proc=${proc:+$proc,}'BISECT'
+
+ # Collect symbols representing repository state
+ local state
+
+ # Upstream HEAD has commits after local HEAD; we're "behind"
+ local -i behind
+ behind=$(git rev-list --count 'HEAD..@{u}' 2>/dev/null)
+ ((behind)) && state=${state}'<'
+
+ # Local HEAD has commits after upstream HEAD; we're "ahead"
+ local -i ahead
+ ahead=$(git rev-list --count '@{u}..HEAD' 2>/dev/null)
+ ((ahead)) && state=${state}'>'
+
+ # Tracked files are modified
+ git diff-files --no-ext-diff --quiet ||
+ state=${state}'!'
+
+ # Changes are staged
+ git diff-index --cached --no-ext-diff --quiet HEAD 2>/dev/null ||
+ state=${state}'+'
+
+ # There are some untracked and unignored files
+ git ls-files --directory --error-unmatch --exclude-standard \
+ --no-empty-directory --others -- ':/*' >/dev/null 2>&1 &&
+ state=${state}'?'
+
+ # There are stashed changes
+ git rev-parse --quiet --verify refs/stash >/dev/null &&
+ state=${state}'^'
+
+ # Print the status in brackets; add a git: prefix only if there
+ # might be another VCS prompt (because PROMPT_VCS is set)
+ printf '(%s%s%s%s)' \
+ "${PROMPT_VCS:+git:}" "${branch:-unknown}" \
+ "${proc:+:$proc}" "$state"
+ ;;
+
+ # Subversion prompt function
+ svn)
+ # Determine the repository URL and root directory
+ local key value url root
+ while IFS=: read -r key value ; do
+ case $key in
+ 'URL')
+ url=${value## }
+ ;;
+ 'Repository Root')
+ root=${value## }
+ ;;
+ esac
+ done < <(svn info 2>/dev/null)
+
+ # Exit if we couldn't get either--or, implicitly, if we don't have
+ # svn(1).
+ [[ -n $url ]] || return
+ [[ -n $root ]] || return
+
+ # Remove the root from the URL to get what's hopefully the branch
+ # name, removing leading slashes and the 'branches' prefix, and any
+ # trailing content after a slash
+ local branch
+ branch=${url/"$root"}
+ branch=${branch#/}
+ branch=${branch#branches/}
+ branch=${branch%%/*}
+
+ # Parse the output of svn status to determine working copy state
+ local symbol
+ local -i modified untracked
+ while read -r symbol _ ; do
+ if [[ $symbol == *'?'* ]] ; then
+ untracked=1
+ else
+ modified=1
+ fi
+ done < <(svn status 2>/dev/null)
+
+ # Add appropriate state flags
+ local -a state
+ ((modified)) && state[${#state[@]}]='!'
+ ((untracked)) && state[${#state[@]}]='?'
+
+ # Print the state in brackets with an svn: prefix
+ (IFS= ; printf '(svn:%s%s)' \
+ "${branch:-unknown}" "${state[*]}")
+ ;;
+
+ # VCS wrapper prompt function; print the first relevant prompt, if any
+ vcs)
+ local vcs
+ for vcs in "${PROMPT_VCS[@]:-git}" ; do
+ prompt "$vcs" && return
+ done
+ ;;
+
+ # Show return status of previous command in angle brackets, if not zero
+ ret)
+ ((PROMPT_RETURN)) && printf '<%u>' "$PROMPT_RETURN"
+ ;;
+
+ # Show the count of background jobs in curly brackets, if not zero
+ job)
+ local -i jobc
+ while read -r ; do
+ ((jobc++))
+ done < <(jobs -p)
+ ((jobc)) && printf '{%u}' "$jobc"
+ ;;
+
+ # No argument given, print prompt strings and vars
+ '')
+ declare -p PS1 PS2 PS3 PS4
+ ;;
+
+ # Print error
+ *)
+ printf 'prompt: Unknown command %s\n' "$1" >&2
+ return 2
+ ;;
+ esac
+}
+
+# Start with full-fledged prompt
+prompt on