Última atividade 1788255366

# shell-prompt.zsh # Powerline-style prompt untuk Zsh di macOS Tahoe. # Diadaptasi dari konfigurasi synth-shell Bash.

Revisão 93c31c53a93bb01c99b78ee062234134cf954f4a

shell-prompt.zsh Bruto
1# shell-prompt.zsh
2# Powerline-style prompt untuk Zsh di macOS Tahoe.
3# Diadaptasi dari konfigurasi synth-shell Bash.
4
5[[ -o interactive ]] || return
6
7# -----------------------------------------------------------------------------
8# Konfigurasi tampilan
9# -----------------------------------------------------------------------------
10SSP_FORMAT=(USER HOST PWD GIT PYENV TF KUBE DATE CLOCK)
11SSP_SEPARATOR_CHAR=$'\uE0B0'
12SSP_SEGMENT_PADDING=' '
13SSP_MAX_PWD_CHAR=25
14
15SSP_GIT_SYNCED=''
16SSP_GIT_AHEAD=' ▲'
17SSP_GIT_BEHIND=' ▼'
18SSP_GIT_DIVERGED=' ◆'
19SSP_GIT_DIRTY=' ◔'
20SSP_GIT_DIRTY_AHEAD=' ◔ △'
21SSP_GIT_DIRTY_BEHIND=' ◔ ▽'
22SSP_GIT_DIRTY_DIVERGED=' ◔ ◇'
23SSP_GIT_STASH='🗎'
24SSP_GIT_UPDATE_PERIOD_MINUTES=15
25
26# color-name -> ANSI foreground/background base codes.
27_ssp_8bit_code() {
28 case "$1" in
29 default|none) echo 9 ;;
30 black) echo 0 ;;
31 red) echo 1 ;;
32 green) echo 2 ;;
33 yellow) echo 3 ;;
34 blue) echo 4 ;;
35 magenta|purple|pink) echo 5 ;;
36 cyan) echo 6 ;;
37 light-gray) echo 7 ;;
38 dark-gray) echo 60 ;;
39 light-red) echo 61 ;;
40 light-green) echo 62 ;;
41 light-yellow) echo 63 ;;
42 light-blue) echo 64 ;;
43 light-magenta|light-purple) echo 65 ;;
44 light-cyan) echo 66 ;;
45 white) echo 67 ;;
46 *) echo 0 ;;
47 esac
48}
49
50_ssp_fg_code() {
51 local color="$1"
52 if [[ "$color" == <-> ]] && (( color >= 0 && color < 256 )); then
53 printf '38;5;%s' "$color"
54 else
55 local bit=$(_ssp_8bit_code "$color")
56 (( bit == 9 )) && printf '39' || printf '%d' $((bit + 30))
57 fi
58}
59
60_ssp_bg_code() {
61 local color="$1"
62 if [[ "$color" == <-> ]] && (( color >= 0 && color < 256 )); then
63 printf '48;5;%s' "$color"
64 else
65 local bit=$(_ssp_8bit_code "$color")
66 (( bit == 9 )) && printf '49' || printf '%d' $((bit + 40))
67 fi
68}
69
70_ssp_effect_code() {
71 case "$1" in
72 bold|bright) echo 1 ;;
73 dim) echo 2 ;;
74 underline) echo 4 ;;
75 blink) echo 5 ;;
76 reverse) echo 7 ;;
77 hidden) echo 8 ;;
78 strikeout) echo 9 ;;
79 *) echo 0 ;;
80 esac
81}
82
83_ssp_ansi() {
84 printf '%%{\033[%sm%%}' "$1"
85}
86
87# -----------------------------------------------------------------------------
88# Warna tiap segment: foreground background effect
89# -----------------------------------------------------------------------------
90typeset -gA SSP_COLOR_FG SSP_COLOR_BG SSP_COLOR_EFFECT
91SSP_COLOR_FG[USER]='black'; SSP_COLOR_BG[USER]='green'; SSP_COLOR_EFFECT[USER]='bold'
92SSP_COLOR_FG[HOST]='white'; SSP_COLOR_BG[HOST]='light-blue'; SSP_COLOR_EFFECT[HOST]='bold'
93SSP_COLOR_FG[PWD]='dark-gray'; SSP_COLOR_BG[PWD]='white'; SSP_COLOR_EFFECT[PWD]='bold'
94SSP_COLOR_FG[GIT]='light-gray'; SSP_COLOR_BG[GIT]='dark-gray'; SSP_COLOR_EFFECT[GIT]='bold'
95SSP_COLOR_FG[PYENV]='white'; SSP_COLOR_BG[PYENV]='blue'; SSP_COLOR_EFFECT[PYENV]='bold'
96SSP_COLOR_FG[KUBE]='white'; SSP_COLOR_BG[KUBE]='purple'; SSP_COLOR_EFFECT[KUBE]='bold'
97SSP_COLOR_FG[TF]='purple'; SSP_COLOR_BG[TF]='light-purple'; SSP_COLOR_EFFECT[TF]='bold'
98SSP_COLOR_FG[DATE]='white'; SSP_COLOR_BG[DATE]='light-purple'; SSP_COLOR_EFFECT[DATE]='bold'
99SSP_COLOR_FG[CLOCK]='white'; SSP_COLOR_BG[CLOCK]='black'; SSP_COLOR_EFFECT[CLOCK]='bold'
100SSP_COLOR_FG[INPUT]='45'; SSP_COLOR_BG[INPUT]='none'; SSP_COLOR_EFFECT[INPUT]='bold'
101
102_ssp_git_branch() {
103 command -v git >/dev/null 2>&1 || return
104 git rev-parse --is-inside-work-tree >/dev/null 2>&1 || return
105
106 local branch
107 branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null) || return
108
109 # Periodic fetch. macOS memakai stat -f, bukan GNU stat -c.
110 if (( SSP_GIT_UPDATE_PERIOD_MINUTES >= 0 )); then
111 local gitdir fetch_head last_update now elapsed
112 gitdir=$(git rev-parse --git-dir 2>/dev/null)
113 [[ "$gitdir" != /* ]] && gitdir="$PWD/$gitdir"
114 fetch_head="$gitdir/FETCH_HEAD"
115 if [[ -f "$fetch_head" ]]; then
116 last_update=$(stat -f '%m' "$fetch_head" 2>/dev/null)
117 now=$(date +%s)
118 if [[ -n "$last_update" ]]; then
119 elapsed=$(( (now - last_update) / 60 ))
120 if (( elapsed >= SSP_GIT_UPDATE_PERIOD_MINUTES )); then
121 (git fetch --recurse-submodules >/dev/null 2>&1 &!)
122 fi
123 fi
124 fi
125 fi
126
127 local porcelain tracking dirty=false ahead=false behind=false symbol=''
128 porcelain=$(git status --porcelain 2>/dev/null)
129 [[ -n "$porcelain" ]] && dirty=true
130 tracking=$(git status --porcelain -uno -b 2>/dev/null)
131 [[ "$tracking" == *ahead* ]] && ahead=true
132 [[ "$tracking" == *behind* ]] && behind=true
133
134 if $dirty && $ahead && $behind; then
135 symbol=$SSP_GIT_DIRTY_DIVERGED
136 elif $dirty && $ahead; then
137 symbol=$SSP_GIT_DIRTY_AHEAD
138 elif $dirty && $behind; then
139 symbol=$SSP_GIT_DIRTY_BEHIND
140 elif $dirty; then
141 symbol=$SSP_GIT_DIRTY
142 elif $ahead && $behind; then
143 symbol=$SSP_GIT_DIVERGED
144 elif $ahead; then
145 symbol=$SSP_GIT_AHEAD
146 elif $behind; then
147 symbol=$SSP_GIT_BEHIND
148 else
149 symbol=$SSP_GIT_SYNCED
150 fi
151
152 local stash_count stash='' tags tag=''
153 stash_count=$(git stash list 2>/dev/null | wc -l | tr -d ' ')
154 (( stash_count > 0 )) && stash=" ${stash_count}${SSP_GIT_STASH}"
155 tags=$(git tag --points-at HEAD 2>/dev/null | paste -sd ',' -)
156 [[ -n "$tags" ]] && tag=" $tags"
157
158 printf '%s%s%s%s' "$branch" "$symbol" "$stash" "$tag"
159}
160
161_ssp_terraform() {
162 command -v terraform >/dev/null 2>&1 || return
163 [[ -d .terraform ]] || return
164 terraform workspace show 2>/dev/null | tr -d '\n'
165}
166
167_ssp_pyenv() {
168 if [[ -n "${CONDA_DEFAULT_ENV:-}" ]]; then
169 printf '%s' "$CONDA_DEFAULT_ENV"
170 elif [[ -n "${VIRTUAL_ENV:-}" ]]; then
171 basename "$VIRTUAL_ENV"
172 fi
173}
174
175_ssp_kube() {
176 command -v kubectl >/dev/null 2>&1 || return
177 local ctx cluster
178 ctx=$(kubectl config current-context 2>/dev/null) || return
179 [[ -n "$ctx" ]] || return
180 cluster=$(kubectl config view -o "jsonpath={.contexts[?(@.name==\"$ctx\")].context.cluster}" 2>/dev/null)
181 [[ -n "$cluster" ]] && printf '%s' "$cluster" || printf '%s' "$ctx"
182}
183
184_ssp_indonesian_date() {
185 local day month hari bulan
186 day=$(LC_ALL=C date +%A)
187 month=$(LC_ALL=C date +%B)
188
189 case "$day" in
190 Monday) hari='Senin' ;;
191 Tuesday) hari='Selasa' ;;
192 Wednesday) hari='Rabu' ;;
193 Thursday) hari='Kamis' ;;
194 Friday) hari="Jum'at" ;;
195 Saturday) hari='Sabtu' ;;
196 Sunday) hari='Ahad' ;;
197 *) hari="$day" ;;
198 esac
199
200 case "$month" in
201 January) bulan='Januari' ;;
202 February) bulan='Februari' ;;
203 March) bulan='Maret' ;;
204 April) bulan='April' ;;
205 May) bulan='Mei' ;;
206 June) bulan='Juni' ;;
207 July) bulan='Juli' ;;
208 August) bulan='Agustus' ;;
209 September) bulan='September' ;;
210 October) bulan='Oktober' ;;
211 November) bulan='November' ;;
212 December) bulan='Desember' ;;
213 *) bulan="$month" ;;
214 esac
215
216 printf '%s, %s %s %s' "$hari" "$(date +%d)" "$bulan" "$(date +%Y)"
217}
218
219_ssp_short_pwd() {
220 local p="$PWD"
221 p=${p/#$HOME/~}
222 if (( ${#p} > SSP_MAX_PWD_CHAR )); then
223 printf '…%s' "${p: -$((SSP_MAX_PWD_CHAR - 1))}"
224 else
225 printf '%s' "$p"
226 fi
227}
228
229_ssp_segment_text() {
230 case "$1" in
231 USER) printf '%s' "$USER" ;;
232 HOST) hostname -s 2>/dev/null || printf '%s' "$HOST" ;;
233 PWD) _ssp_short_pwd ;;
234 GIT) _ssp_git_branch ;;
235 PYENV) _ssp_pyenv ;;
236 TF) _ssp_terraform ;;
237 KUBE) _ssp_kube ;;
238 DATE) _ssp_indonesian_date ;;
239 CLOCK) date +%H:%M:%S ;;
240 esac
241}
242
243_ssp_print_segment() {
244 local element="$1" text="$2" next_bg="$3"
245 local fg=${SSP_COLOR_FG[$element]}
246 local bg=${SSP_COLOR_BG[$element]}
247 local effect=${SSP_COLOR_EFFECT[$element]}
248 local text_code sep_code reset
249
250 text_code=$(_ssp_ansi "$(_ssp_fg_code "$fg");$(_ssp_bg_code "$bg");$(_ssp_effect_code "$effect")")
251 sep_code=$(_ssp_ansi "$(_ssp_fg_code "$bg");$(_ssp_bg_code "$next_bg")")
252 reset=$(_ssp_ansi '0')
253
254 printf '%s%s%s%s%s%s%s' \
255 "$text_code" "$SSP_SEGMENT_PADDING" "$text" "$SSP_SEGMENT_PADDING" \
256 "$sep_code" "$SSP_SEPARATOR_CHAR" "$reset"
257}
258
259_ssp_build_prompt() {
260 local -a active
261 local element text i current next next_bg prompt=''
262
263 for element in "${SSP_FORMAT[@]}"; do
264 text=$(_ssp_segment_text "$element")
265 [[ -n "$text" ]] && active+=("$element:$text")
266 done
267
268 for (( i=1; i<=${#active[@]}; i++ )); do
269 current=${active[$i]}
270 element=${current%%:*}
271 text=${current#*:}
272
273 if (( i < ${#active[@]} )); then
274 next=${active[$((i+1))]}
275 next=${next%%:*}
276 next_bg=${SSP_COLOR_BG[$next]}
277 else
278 next_bg='none'
279 fi
280 prompt+=$(_ssp_print_segment "$element" "$text" "$next_bg")
281 done
282
283 local input_code reset
284 input_code=$(_ssp_ansi "$(_ssp_fg_code "${SSP_COLOR_FG[INPUT]}");$(_ssp_bg_code "${SSP_COLOR_BG[INPUT]}");$(_ssp_effect_code "${SSP_COLOR_EFFECT[INPUT]}")")
285 reset=$(_ssp_ansi '0')
286
287 # Dua baris seperti prompt Debian: ╭ segments ... lalu ╰>$
288 PROMPT=$'\n╭'"${prompt}"$'\n╰>'"${input_code}"'$ '"${reset}"
289
290 # Judul terminal.
291 print -Pn "\e]0;${USER}@$(hostname -s): ${PWD}\a"
292}
293
294# Hook native Zsh, menggantikan PROMPT_COMMAND milik Bash.
295autoload -Uz add-zsh-hook
296add-zsh-hook precmd _ssp_build_prompt
297
298# Bangun prompt pertama kali saat file di-source.
299_ssp_build_prompt
300