|
| 1 | +#!/bin/bash |
| 2 | +# Bash-specific prompt implementation |
| 3 | + |
| 4 | +# Variables PROMPT_GIT_* and PROMPT_CMD_DURATION are set by sourced helpers |
| 5 | +# shellcheck disable=SC2154 |
| 6 | + |
| 7 | +# shellcheck source=git_info.sh |
| 8 | +[ -f "$HOME/.shell_common/prompt/git_info.sh" ] && . "$HOME/.shell_common/prompt/git_info.sh" |
| 9 | +# shellcheck source=cmd_duration.sh |
| 10 | +[ -f "$HOME/.shell_common/prompt/cmd_duration.sh" ] && . "$HOME/.shell_common/prompt/cmd_duration.sh" |
| 11 | + |
| 12 | +# --- Command duration via DEBUG trap --- |
| 13 | +# Only set if no existing DEBUG trap to avoid conflicts |
| 14 | +if [ -z "$(trap -p DEBUG)" ]; then |
| 15 | + trap '__prompt_cmd_timer_start' DEBUG |
| 16 | + _PROMPT_HAS_DURATION=1 |
| 17 | +fi |
| 18 | + |
| 19 | +# --- Color helpers for bash (non-printing escape wrappers) --- |
| 20 | +_c_reset='\[\033[0m\]' |
| 21 | +_c_bold='\[\033[1m\]' |
| 22 | +_c_green='\[\033[32m\]' |
| 23 | +_c_red='\[\033[31m\]' |
| 24 | +_c_cyan='\[\033[36m\]' |
| 25 | +_c_blue='\[\033[34m\]' |
| 26 | +_c_yellow='\[\033[33m\]' |
| 27 | +_c_bold_green='\[\033[1;32m\]' |
| 28 | +_c_bold_red='\[\033[1;31m\]' |
| 29 | +_c_bold_cyan='\[\033[1;36m\]' |
| 30 | +_c_bold_blue='\[\033[1;34m\]' |
| 31 | + |
| 32 | +# --- Main prompt builder --- |
| 33 | +__prompt_bash_build() { |
| 34 | + local exit_code=$? |
| 35 | + |
| 36 | + # Run shared helpers |
| 37 | + __prompt_git_info |
| 38 | + if [ "$_PROMPT_HAS_DURATION" = "1" ]; then |
| 39 | + __prompt_cmd_timer_stop |
| 40 | + fi |
| 41 | + |
| 42 | + # Line 1: directory user@host status [duration] |
| 43 | + local dir="${_c_bold_green}\w${_c_reset}" |
| 44 | + local user |
| 45 | + if [ "$(id -u)" -eq 0 ]; then |
| 46 | + user="${_c_bold_red}\u${_c_reset}" |
| 47 | + else |
| 48 | + user="${_c_bold_cyan}\u${_c_reset}" |
| 49 | + fi |
| 50 | + local host="${_c_bold_blue}\h${_c_reset}" |
| 51 | + |
| 52 | + local status_icon |
| 53 | + if [ "$exit_code" -eq 0 ]; then |
| 54 | + status_icon='✅' |
| 55 | + else |
| 56 | + status_icon='❌'" ${_c_red}${exit_code}${_c_reset}" |
| 57 | + fi |
| 58 | + |
| 59 | + local duration="" |
| 60 | + if [ -n "$PROMPT_CMD_DURATION" ]; then |
| 61 | + duration=" ${_c_yellow}took ${PROMPT_CMD_DURATION}${_c_reset}" |
| 62 | + fi |
| 63 | + |
| 64 | + local line1="${dir} ${user}${_c_yellow}@${_c_reset}${host} ${status_icon}${duration}" |
| 65 | + |
| 66 | + # Line 2: git info (only in git repos) |
| 67 | + local line2="" |
| 68 | + if [ "$PROMPT_GIT_ACTIVE" = "1" ]; then |
| 69 | + local git_parts="${_c_bold_cyan}${PROMPT_GIT_BRANCH}${_c_reset}" |
| 70 | + if [ -n "$PROMPT_GIT_REMOTE" ]; then |
| 71 | + git_parts="${git_parts} ${_c_blue}→ ${PROMPT_GIT_REMOTE}${_c_reset}" |
| 72 | + fi |
| 73 | + |
| 74 | + # State (merge, rebase, etc.) |
| 75 | + if [ -n "$PROMPT_GIT_STATE" ]; then |
| 76 | + git_parts="${git_parts} ${_c_bold_red}(${PROMPT_GIT_STATE})${_c_reset}" |
| 77 | + fi |
| 78 | + |
| 79 | + # Ahead/behind |
| 80 | + local ab="" |
| 81 | + if [ -n "$PROMPT_GIT_AHEAD" ] && [ -n "$PROMPT_GIT_BEHIND" ]; then |
| 82 | + ab="⇕⇡${PROMPT_GIT_AHEAD}⇣${PROMPT_GIT_BEHIND}" |
| 83 | + elif [ -n "$PROMPT_GIT_AHEAD" ]; then |
| 84 | + ab="⇡${PROMPT_GIT_AHEAD}" |
| 85 | + elif [ -n "$PROMPT_GIT_BEHIND" ]; then |
| 86 | + ab="⇣${PROMPT_GIT_BEHIND}" |
| 87 | + fi |
| 88 | + if [ -n "$ab" ]; then |
| 89 | + git_parts="${git_parts} ${_c_cyan}[${ab}]${_c_reset}" |
| 90 | + fi |
| 91 | + |
| 92 | + # Clean indicator |
| 93 | + if [ "$PROMPT_GIT_STAGED" -eq 0 ] && [ "$PROMPT_GIT_MODIFIED" -eq 0 ] && \ |
| 94 | + [ "$PROMPT_GIT_UNTRACKED" -eq 0 ] && [ "$PROMPT_GIT_DELETED" -eq 0 ]; then |
| 95 | + git_parts="${git_parts} ✨" |
| 96 | + fi |
| 97 | + |
| 98 | + # File status counts |
| 99 | + if [ "$PROMPT_GIT_STAGED" -gt 0 ]; then |
| 100 | + git_parts="${git_parts} ${_c_green}+${PROMPT_GIT_STAGED} staged${_c_reset}" |
| 101 | + fi |
| 102 | + if [ "$PROMPT_GIT_MODIFIED" -gt 0 ]; then |
| 103 | + git_parts="${git_parts} ${_c_yellow}!${PROMPT_GIT_MODIFIED} modified${_c_reset}" |
| 104 | + fi |
| 105 | + if [ "$PROMPT_GIT_DELETED" -gt 0 ]; then |
| 106 | + git_parts="${git_parts} ${_c_red}✘${PROMPT_GIT_DELETED} deleted${_c_reset}" |
| 107 | + fi |
| 108 | + if [ "$PROMPT_GIT_UNTRACKED" -gt 0 ]; then |
| 109 | + git_parts="${git_parts} ${_c_red}?${PROMPT_GIT_UNTRACKED} untracked${_c_reset}" |
| 110 | + fi |
| 111 | + |
| 112 | + line2=$'\n'"${git_parts}" |
| 113 | + fi |
| 114 | + |
| 115 | + # Line 3: prompt character |
| 116 | + local mark |
| 117 | + if [ "$exit_code" -eq 0 ]; then |
| 118 | + mark="${_c_bold_green}>${_c_reset} " |
| 119 | + else |
| 120 | + mark="${_c_bold_red}>${_c_reset} " |
| 121 | + fi |
| 122 | + |
| 123 | + # Add newline before prompt (except first prompt) |
| 124 | + local newline="" |
| 125 | + if [ -z "$_PROMPT_FIRST" ]; then |
| 126 | + _PROMPT_FIRST=1 |
| 127 | + else |
| 128 | + newline=$'\n' |
| 129 | + fi |
| 130 | + |
| 131 | + PS1="${newline}${line1}${line2}"$'\n'"${mark}" |
| 132 | +} |
| 133 | + |
| 134 | +# Append to PROMPT_COMMAND without overwriting existing entries |
| 135 | +if [ -n "$PROMPT_COMMAND" ]; then |
| 136 | + PROMPT_COMMAND="__prompt_bash_build;${PROMPT_COMMAND}" |
| 137 | +else |
| 138 | + PROMPT_COMMAND="__prompt_bash_build" |
| 139 | +fi |
0 commit comments