Naposledy aktivní 1788255366

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

Revize 591bfb6a7dd7a8de8076d82a860070536c4a283e

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