forked from sindresorhus/pure
-
Notifications
You must be signed in to change notification settings - Fork 0
/
simpl.zsh
742 lines (616 loc) · 23.7 KB
/
simpl.zsh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
# Simpl
# by Eduardo Ruiz
# https://github.com/eduarbo/simpl
# MIT License
# Simpl theme heavily inspired by Sindre Sorhus' Pure theme
# https://github.com/sindresorhus/pure
# For my own and others sanity
# git:
# %b => current branch
# %a => current action (rebase/merge)
# prompt:
# %B => Start boldface mode.
# %b => Stop boldface mode.
# %U => Start underline mode.
# %u => Stop underline mode.
# %S => Start standout mode.
# %s => Stop standout mode.
# %K => Start using a different bacKground colour. The syntax is identical to
# that for %F and %f.
# %k => Stop using a different bacKground colour.
# %F => Start using a different foreground colour, if supported by the terminal.
# The colour may be specified two ways: either as a numeric argument, as
# normal, or by a sequence in braces following the %F, for example
# %F{red}. In the latter case the values allowed are as described for the
# fg zle_highlight attribute; Character Highlighting. This means that
# numeric colours are allowed in the second format also.
# %f => Reset foreground color.
# %~ => current path
# %* => time
# %n => username
# %m => shortname host
# Conditional Substrings in Prompts:
# %(x.true-text.false-text) => Specifies a ternary expression
# The left parenthesis may be preceded or followed by a positive integer n,
# which defaults to zero. A negative integer will be multiplied by -1. The
# test character x may be any of the following:
# ! => True if the shell is running with privileges
# ? => True if the exit status of the last command was n
# # => True if the effective uid of the current process is n
# terminal codes:
# \e7 => save cursor position
# \e[2A => move cursor 2 lines up
# \e[1G => go to position 1 in terminal
# \e8 => restore cursor position
# \e[K => clears everything after the cursor on the current line
# \e[2K => clear everything on the current line
# Configuration
# Set options in the SIMPL namespace to not pollute the global namespace
: ${SIMPL_ALWAYS_SHOW_USER:=0}
: ${SIMPL_ALWAYS_SHOW_USER_AND_HOST:=0}
: ${SIMPL_CMD_MAX_EXEC_TIME:=5}
: ${SIMPL_ENABLE_RPROMPT:=0}
: ${SIMPL_GIT_DELAY_DIRTY_CHECK:=1800}
: ${SIMPL_GIT_PULL:=1}
: ${SIMPL_GIT_UNTRACKED_DIRTY:=1}
# symbols
: ${SIMPL_GIT_DIRTY_SYMBOL:="*"}
: ${SIMPL_GIT_DOWN_ARROW:="⇣"}
: ${SIMPL_GIT_UP_ARROW:="⇡"}
: ${SIMPL_JOBS_SYMBOL:="↻"}
: ${SIMPL_PROMPT_ROOT_SYMBOL:="#"}
: ${SIMPL_PROMPT_SYMBOL:="❱"}
# colors
: ${SIMPL_DIR_COLOR:="%F{cyan}"}
: ${SIMPL_EXEC_TIME_COLOR:="%B%F{8}"}
: ${SIMPL_GIT_ARROW_COLOR:="%B%F{blue}"}
: ${SIMPL_GIT_BRANCH_COLOR:="%F{14}"}
: ${SIMPL_GIT_DIRTY_COLOR:="%F{9}"}
: ${SIMPL_HOST_COLOR:="%F{11}"}
: ${SIMPL_HOST_SYMBOL_COLOR:="%F{yellow}"}
: ${SIMPL_JOBS_COLOR:="%B%F{8}"}
: ${SIMPL_PREPOSITION_COLOR:="%F{8}"}
: ${SIMPL_PROMPT_SYMBOL_COLOR:="%F{yellow}"}
: ${SIMPL_PROMPT_SYMBOL_ERROR_COLOR:="%F{red}"}
: ${SIMPL_PROMPT2_SYMBOL_COLOR:="%F{8}"}
: ${SIMPL_USER_COLOR:="%F{11}"}
: ${SIMPL_USER_ROOT_COLOR:="%B%F{red}"}
: ${SIMPL_VENV_COLOR:="%F{11}"}
# Utils
cl="%f%s%u%k%b"
# turns seconds into human readable time
# 165392 => 1d 21h 56m 32s
# https://github.com/sindresorhus/pretty-time-zsh
_prompt_simpl_human_time_to_var() {
local human total_seconds=$1 var=$2
local days=$(( total_seconds / 60 / 60 / 24 ))
local hours=$(( total_seconds / 60 / 60 % 24 ))
local minutes=$(( total_seconds / 60 % 60 ))
local seconds=$(( total_seconds % 60 ))
(( days > 0 )) && human+="${days}d "
(( hours > 0 )) && human+="${hours}h "
(( minutes > 0 )) && human+="${minutes}m "
human+="${seconds}s"
# store human readable time in variable as specified by caller
typeset -g "${var}"="${human}"
}
# stores (into prompt_simpl_cmd_exec_time) the exec time of the last command if set threshold was exceeded
_prompt_simpl_check_cmd_exec_time() {
integer elapsed
(( elapsed = EPOCHSECONDS - ${prompt_simpl_cmd_timestamp:-$EPOCHSECONDS} ))
typeset -g prompt_simpl_cmd_exec_time=
(( elapsed > $SIMPL_CMD_MAX_EXEC_TIME )) && {
_prompt_simpl_human_time_to_var $elapsed "prompt_simpl_cmd_exec_time"
}
}
_prompt_simpl_set_title() {
setopt localoptions noshwordsplit
# emacs terminal does not support settings the title
(( ${+EMACS} )) && return
case $TTY in
# Don't set title over serial console.
/dev/ttyS[0-9]*) return;;
esac
# Show hostname if connected via ssh.
local hostname=
if [[ -n $prompt_simpl_state[username] ]]; then
# Expand in-place in case ignore-escape is used.
hostname="${(%):-(%m) }"
fi
local -a opts
case $1 in
expand-prompt) opts=(-P);;
ignore-escape) opts=(-r);;
esac
# Set title atomically in one print statement so that it works
# when XTRACE is enabled.
print -n $opts $'\e]0;'${hostname}${2}$'\a'
}
_prompt_simpl_preexec() {
if [[ -n $prompt_simpl_git_fetch_pattern ]]; then
# detect when git is performing pull/fetch (including git aliases).
local -H MATCH MBEGIN MEND match mbegin mend
if [[ $2 =~ (git|hub)\ (.*\ )?($prompt_simpl_git_fetch_pattern)(\ .*)?$ ]]; then
# we must flush the async jobs to cancel our git fetch in order
# to avoid conflicts with the user issued pull / fetch.
async_flush_jobs 'prompt_simpl'
fi
fi
typeset -g prompt_simpl_cmd_timestamp=$EPOCHSECONDS
# shows the current dir and executed command in the title while a process is active
_prompt_simpl_set_title 'ignore-escape' "$PWD:t: $2"
# Disallow python virtualenv from updating the prompt, set it to 12 if
# untouched by the user to indicate that Simpl modified it. Here we use
# magic number 12, same as in psvar.
export VIRTUAL_ENV_DISABLE_PROMPT=${VIRTUAL_ENV_DISABLE_PROMPT:-12}
}
_prompt_simpl_preprompt_render() {
setopt localoptions noshwordsplit
# Initialize the preprompt array.
local -a preprompt_parts
# Username and machine, if applicable.
if [[ -n $prompt_simpl_state[username] ]] && (( ! $SIMPL_ENABLE_RPROMPT )); then
local in="${SIMPL_PREPOSITION_COLOR}in${cl}"
preprompt_parts+=("${prompt_simpl_state[username]} ${in}")
fi
# Set the path.
preprompt_parts+=("${SIMPL_DIR_COLOR}%~${cl}")
# Add git branch and dirty status info.
typeset -gA prompt_simpl_vcs_info
# Git pull/push arrows.
if [[ -n $prompt_simpl_vcs_info[branch] ]]; then
# Set color for git branch/dirty status, change color if dirty checking has
# been delayed.
local branch_color="${SIMPL_GIT_BRANCH_COLOR}"
[[ -n ${prompt_simpl_git_last_dirty_check_timestamp+x} ]] && branch_color="${cl}%F{red}"
preprompt_parts+=("${SIMPL_PREPOSITION_COLOR}on${cl}")
if [[ -n $prompt_simpl_git_arrows ]]; then
preprompt_parts+=("${SIMPL_GIT_ARROW_COLOR}${prompt_simpl_git_arrows}${cl}")
fi
preprompt_parts+=("${branch_color}${prompt_simpl_vcs_info[branch]}${cl}${prompt_simpl_git_dirty}")
fi
# Number of jobs in background.
if [[ -n $(jobs) ]]; then
preprompt_parts+=("${SIMPL_JOBS_COLOR}${SIMPL_JOBS_SYMBOL}%(1j.%j.)${cl}")
fi
# Execution time.
if [[ -n $prompt_simpl_cmd_exec_time ]]; then
preprompt_parts+=("${SIMPL_EXEC_TIME_COLOR}${prompt_simpl_cmd_exec_time}${cl}")
fi
local cleaned_ps1=$PROMPT
local -H MATCH MBEGIN MEND
if [[ $PROMPT = *$prompt_newline* ]]; then
# Remove everything from the prompt until the newline. This
# removes the preprompt and only the original PROMPT remains.
cleaned_ps1=${PROMPT##*${prompt_newline}}
fi
unset MATCH MBEGIN MEND
# Construct the new prompt with a clean preprompt.
local -ah ps1
ps1=(
${(j. .)preprompt_parts} # Join parts, space separated.
$prompt_newline # Separate preprompt and prompt.
$cleaned_ps1
)
PROMPT="${(j..)ps1}"
# Expand the prompt for future comparision.
local expanded_prompt
expanded_prompt="${(S%%)PROMPT}"
if [[ $1 == precmd && $SIMPL_NEWLINE_BEFORE_PROMPT == true ]]; then
# Print a newline before the prompt, unless it's the first prompt in the
# parent process
print
elif [[ $prompt_simpl_last_prompt != $expanded_prompt ]]; then
# Redraw the prompt.
zle && zle .reset-prompt
fi
SIMPL_NEWLINE_BEFORE_PROMPT=true
typeset -g prompt_simpl_last_prompt=$expanded_prompt
}
_prompt_simpl_precmd() {
# check exec time and store it in a variable
_prompt_simpl_check_cmd_exec_time
unset prompt_simpl_cmd_timestamp
# shows the full path in the title
_prompt_simpl_set_title 'expand-prompt' '%~'
# preform async git dirty check and fetch
_prompt_simpl_async_tasks
# Check if we should display the virtual env, we use a sufficiently high
# index of psvar (12) here to avoid collisions with user defined entries.
psvar[12]=
# Check if a conda environment is active and display it's name
if [[ -n $CONDA_DEFAULT_ENV ]]; then
psvar[12]="${CONDA_DEFAULT_ENV//[$'\t\r\n']}"
fi
# When VIRTUAL_ENV_DISABLE_PROMPT is empty, it was unset by the user and
# Simpl should take back control.
if [[ -n $VIRTUAL_ENV ]] && [[ -z $VIRTUAL_ENV_DISABLE_PROMPT || $VIRTUAL_ENV_DISABLE_PROMPT = 12 ]]; then
psvar[12]="${VIRTUAL_ENV:t}"
export VIRTUAL_ENV_DISABLE_PROMPT=12
fi
# print the preprompt
_prompt_simpl_preprompt_render "precmd"
if [[ -n $ZSH_THEME ]]; then
print "WARNING: Oh My Zsh themes are enabled (ZSH_THEME='${ZSH_THEME}'). Simpl might not be working correctly."
print "For more information, see: https://github.com/eduarbo/simpl#oh-my-zsh"
unset ZSH_THEME # Only show this warning once.
fi
}
_prompt_simpl_async_git_aliases() {
setopt localoptions noshwordsplit
local -a gitalias pullalias
# list all aliases and split on newline.
gitalias=(${(@f)"$(command git config --get-regexp "^alias\.")"})
for line in $gitalias; do
parts=(${(@)=line}) # split line on spaces
aliasname=${parts[1]#alias.} # grab the name (alias.[name])
shift parts # remove aliasname
# check alias for pull or fetch (must be exact match).
if [[ $parts =~ ^(.*\ )?(pull|fetch)(\ .*)?$ ]]; then
pullalias+=($aliasname)
fi
done
print -- ${(j:|:)pullalias} # join on pipe (for use in regex).
}
_prompt_simpl_async_vcs_info() {
setopt localoptions noshwordsplit
# configure vcs_info inside async task, this frees up vcs_info
# to be used or configured as the user pleases.
zstyle ':vcs_info:*' enable git
zstyle ':vcs_info:*' use-simple true
# only export two msg variables from vcs_info
zstyle ':vcs_info:*' max-exports 2
# export branch (%b) and git toplevel (%R)
zstyle ':vcs_info:git*' formats '%b' '%R'
zstyle ':vcs_info:git*' actionformats '%b|%a' '%R'
vcs_info
local -A info
info[pwd]=$PWD
info[top]=$vcs_info_msg_1_
info[branch]=$vcs_info_msg_0_
print -r - ${(@kvq)info}
}
# fastest possible way to check if repo is dirty
_prompt_simpl_async_git_dirty() {
setopt localoptions noshwordsplit
local untracked_dirty=$1
if [[ $untracked_dirty = 0 ]]; then
command git diff --no-ext-diff --quiet --exit-code
else
test -z "$(command git status --porcelain --ignore-submodules -unormal)"
fi
return $?
}
_prompt_simpl_async_git_fetch() {
setopt localoptions noshwordsplit
# set GIT_TERMINAL_PROMPT=0 to disable auth prompting for git fetch (git 2.3+)
export GIT_TERMINAL_PROMPT=0
# set ssh BachMode to disable all interactive ssh password prompting
export GIT_SSH_COMMAND="${GIT_SSH_COMMAND:-"ssh"} -o BatchMode=yes"
# Default return code, indicates Git fetch failure.
local fail_code=99
# Guard against all forms of password prompts. By setting the shell into
# MONITOR mode we can notice when a child process prompts for user input
# because it will be suspended. Since we are inside an async worker, we
# have no way of transmitting the password and the only option is to
# kill it. If we don't do it this way, the process will corrupt with the
# async worker.
setopt localtraps monitor
# Make sure local HUP trap is unset to allow for signal propagation when
# the async worker is flushed.
trap - HUP
trap '
# Unset trap to prevent infinite loop
trap - CHLD
if [[ $jobstates = suspended* ]]; then
# Set fail code to password prompt and kill the fetch.
fail_code=98
kill %%
fi
' CHLD
command git -c gc.auto=0 fetch >/dev/null &
wait $! || return $fail_code
unsetopt monitor
# check arrow status after a successful git fetch
_prompt_simpl_async_git_arrows
}
_prompt_simpl_async_git_arrows() {
setopt localoptions noshwordsplit
command git rev-list --left-right --count HEAD...@'{u}'
}
_prompt_simpl_async_tasks() {
setopt localoptions noshwordsplit
# initialize async worker
((!${prompt_simpl_async_init:-0})) && {
async_start_worker "prompt_simpl" -u -n
async_register_callback "prompt_simpl" _prompt_simpl_async_callback
typeset -g prompt_simpl_async_init=1
}
# Update the current working directory of the async worker.
async_worker_eval "prompt_simpl" builtin cd -q $PWD
typeset -gA prompt_simpl_vcs_info
local -H MATCH MBEGIN MEND
if [[ $PWD != ${prompt_simpl_vcs_info[pwd]}* ]]; then
# stop any running async jobs
async_flush_jobs "prompt_simpl"
# reset git preprompt variables, switching working tree
unset prompt_simpl_git_dirty
unset prompt_simpl_git_last_dirty_check_timestamp
unset prompt_simpl_git_arrows
unset prompt_simpl_git_fetch_pattern
prompt_simpl_vcs_info[branch]=
prompt_simpl_vcs_info[top]=
fi
unset MATCH MBEGIN MEND
async_job "prompt_simpl" _prompt_simpl_async_vcs_info
# # only perform tasks inside git working tree
[[ -n $prompt_simpl_vcs_info[top] ]] || return
_prompt_simpl_async_refresh
}
_prompt_simpl_async_refresh() {
setopt localoptions noshwordsplit
if [[ -z $prompt_simpl_git_fetch_pattern ]]; then
# we set the pattern here to avoid redoing the pattern check until the
# working three has changed. pull and fetch are always valid patterns.
typeset -g prompt_simpl_git_fetch_pattern="pull|fetch"
async_job "prompt_simpl" _prompt_simpl_async_git_aliases
fi
async_job "prompt_simpl" _prompt_simpl_async_git_arrows
# do not preform git fetch if it is disabled or in home folder.
if (( ${SIMPL_GIT_PULL} )) && [[ $prompt_simpl_vcs_info[top] != $HOME ]]; then
# tell worker to do a git fetch
async_job "prompt_simpl" _prompt_simpl_async_git_fetch
fi
# if dirty checking is sufficiently fast, tell worker to check it again, or wait for timeout
integer time_since_last_dirty_check=$(( EPOCHSECONDS - ${prompt_simpl_git_last_dirty_check_timestamp:-0} ))
if (( time_since_last_dirty_check > ${SIMPL_GIT_DELAY_DIRTY_CHECK} )); then
unset prompt_simpl_git_last_dirty_check_timestamp
# check check if there is anything to pull
async_job "prompt_simpl" _prompt_simpl_async_git_dirty ${SIMPL_GIT_UNTRACKED_DIRTY}
fi
}
_prompt_simpl_check_git_arrows() {
setopt localoptions noshwordsplit
local arrows left=${1:-0} right=${2:-0}
(( right > 0 )) && arrows+=${SIMPL_GIT_DOWN_ARROW}
(( left > 0 )) && arrows+=${SIMPL_GIT_UP_ARROW}
[[ -n $arrows ]] || return
typeset -g REPLY=$arrows
}
_prompt_simpl_async_callback() {
setopt localoptions noshwordsplit
local job=$1 code=$2 output=$3 exec_time=$4 next_pending=$6
local do_render=0
case $job in
\[async])
# code is 1 for corrupted worker output and 2 for dead worker
if [[ $code -eq 2 ]]; then
# our worker died unexpectedly
typeset -g prompt_simpl_async_init=0
fi
;;
_prompt_simpl_async_vcs_info)
local -A info
typeset -gA prompt_simpl_vcs_info
# parse output (z) and unquote as array (Q@)
info=("${(Q@)${(z)output}}")
local -H MATCH MBEGIN MEND
if [[ $info[pwd] != $PWD ]]; then
# The path has changed since the check started, abort.
return
fi
# check if git toplevel has changed
if [[ $info[top] = $prompt_simpl_vcs_info[top] ]]; then
# if stored pwd is part of $PWD, $PWD is shorter and likelier
# to be toplevel, so we update pwd
if [[ $prompt_simpl_vcs_info[pwd] =~ ^$PWD ]]; then
prompt_simpl_vcs_info[pwd]=$PWD
fi
else
# store $PWD to detect if we (maybe) left the git path
prompt_simpl_vcs_info[pwd]=$PWD
fi
unset MATCH MBEGIN MEND
# update has a git toplevel set which means we just entered a new
# git directory, run the async refresh tasks
[[ -n $info[top] ]] && [[ -z $prompt_simpl_vcs_info[top] ]] && _prompt_simpl_async_refresh
# always update branch and toplevel
prompt_simpl_vcs_info[branch]=$info[branch]
prompt_simpl_vcs_info[top]=$info[top]
do_render=1
;;
_prompt_simpl_async_git_aliases)
if [[ -n $output ]]; then
# append custom git aliases to the predefined ones.
prompt_simpl_git_fetch_pattern+="|$output"
fi
;;
_prompt_simpl_async_git_dirty)
local prev_dirty=$prompt_simpl_git_dirty
if (( code == 0 )); then
prompt_simpl_git_dirty=
else
prompt_simpl_git_dirty="${SIMPL_GIT_DIRTY_COLOR}${SIMPL_GIT_DIRTY_SYMBOL}${cl}"
fi
[[ $prev_dirty != $prompt_simpl_git_dirty ]] && do_render=1
# When prompt_simpl_git_last_dirty_check_timestamp is set, the git info is displayed in a different color.
# To distinguish between a "fresh" and a "cached" result, the preprompt is rendered before setting this
# variable. Thus, only upon next rendering of the preprompt will the result appear in a different color.
(( $exec_time > 5 )) && prompt_simpl_git_last_dirty_check_timestamp=$EPOCHSECONDS
;;
_prompt_simpl_async_git_fetch|_prompt_simpl_async_git_arrows)
# _prompt_simpl_async_git_fetch executes _prompt_simpl_async_git_arrows
# after a successful fetch.
case $code in
0)
local REPLY
_prompt_simpl_check_git_arrows ${(ps:\t:)output}
if [[ $prompt_simpl_git_arrows != $REPLY ]]; then
typeset -g prompt_simpl_git_arrows=$REPLY
do_render=1
fi
;;
99|98)
# Git fetch failed.
;;
*)
# Non-zero exit status from _prompt_simpl_async_git_arrows,
# indicating that there is no upstream configured.
if [[ -n $prompt_simpl_git_arrows ]]; then
unset prompt_simpl_git_arrows
do_render=1
fi
;;
esac
;;
esac
if (( next_pending )); then
(( do_render )) && typeset -g prompt_simpl_async_render_requested=1
return
fi
[[ ${prompt_simpl_async_render_requested:-$do_render} = 1 ]] && _prompt_simpl_preprompt_render
unset prompt_simpl_async_render_requested
}
_prompt_simpl_set_cursor_style() {
local my_terms=(xterm-256color xterm-kitty)
# Change cursor shape only on tested $TERMs
if [[ ${my_terms[(ie)$TERM]} -le ${#my_terms} ]]; then
# \e[0 q or \e[ q: reset to whatever's defined in the profile settings
# \e[1 q: blinking block
# \e[2 q: steady block
# \e[3 q: blinking underline
# \e[4 q: steady underline
# \e[5 q: blinking I-beam
# \e[6 q: steady I-beam
case $KEYMAP in
# vi emulation - command mode
vicmd) echo -ne "\e[1 q";;
# vi emulation - insert mode
viins|main) echo -ne "\e[3 q";;
esac
fi
}
_prompt_simpl_state_setup() {
setopt localoptions noshwordsplit
# Check SSH_CONNECTION and the current state.
local ssh_connection=${SSH_CONNECTION:-$PROMPT_SIMPL_SSH_CONNECTION}
if [[ -z $ssh_connection ]] && (( $+commands[who] )); then
# When changing user on a remote system, the $SSH_CONNECTION
# environment variable can be lost, attempt detection via who.
local who_out
who_out=$(who -m 2>/dev/null)
if (( $? )); then
# Who am I not supported, fallback to plain who.
local -a who_in
who_in=( ${(f)"$(who 2>/dev/null)"} )
who_out="${(M)who_in:#*[[:space:]]${TTY#/dev/}[[:space:]]*}"
fi
local reIPv6='(([0-9a-fA-F]+:)|:){2,}[0-9a-fA-F]+' # Simplified, only checks partial pattern.
local reIPv4='([0-9]{1,3}\.){3}[0-9]+' # Simplified, allows invalid ranges.
# Here we assume two non-consecutive periods represents a
# hostname. This matches foo.bar.baz, but not foo.bar.
local reHostname='([.][^. ]+){2}'
# Usually the remote address is surrounded by parenthesis, but
# not on all systems (e.g. busybox).
local -H MATCH MBEGIN MEND
if [[ $who_out =~ "\(?($reIPv4|$reIPv6|$reHostname)\)?\$" ]]; then
ssh_connection=$MATCH
# Export variable to allow detection propagation inside
# shells spawned by this one (e.g. tmux does not always
# inherit the same tty, which breaks detection).
export PROMPT_SIMPL_SSH_CONNECTION=$ssh_connection
fi
unset MATCH MBEGIN MEND
fi
local prompt="%(#.${SIMPL_PROMPT_ROOT_SYMBOL}.${SIMPL_PROMPT_SYMBOL})${cl}"
local user="%(#.${SIMPL_USER_ROOT_COLOR}%n.${SIMPL_USER_COLOR}%n)${cl}"
local username
if (( ${SIMPL_ALWAYS_SHOW_USER} )) || [[ "$SSH_CONNECTION" != '' ]]; then
username="${user}"
fi
# show hostname if connected via ssh or if overridden by option
if (( ${SIMPL_ALWAYS_SHOW_USER_AND_HOST} )) || [[ "$SSH_CONNECTION" != '' ]]; then
local host_symbol="$SIMPL_HOST_SYMBOL_MAP[$( hostname -s )]"
local host
if [[ -n $host_symbol ]]; then
host="${SIMPL_HOST_SYMBOL_COLOR}${host_symbol}${cl}"
username="${host} ${user}"
else
local at="${SIMPL_PREPOSITION_COLOR}at${cl}"
host="${SIMPL_HOST_COLOR}%m${cl}"
username="${user} ${at} ${host}"
fi
fi
typeset -gA prompt_simpl_state
prompt_simpl_state=(
version "2.0.0"
username "${username}"
prompt "${prompt}"
)
}
_prompt_simpl_setup() {
# Prevent percentage showing up if output doesn't end with a newline.
export PROMPT_EOL_MARK=''
prompt_opts=(subst percent)
# borrowed from promptinit, sets the prompt options in case simpl was not
# initialized via promptinit.
setopt noprompt{bang,cr,percent,subst} "prompt${^prompt_opts[@]}"
setopt transientrprompt # only have the rprompt on the last line
if [[ -z $prompt_newline ]]; then
# This variable needs to be set, usually set by promptinit.
typeset -g prompt_newline=$'\n%{\r%}'
fi
zmodload zsh/datetime
zmodload zsh/zle
zmodload zsh/parameter
autoload -Uz add-zsh-hook
autoload -Uz vcs_info
autoload -Uz async && async
# The add-zle-hook-widget function is not guaranteed
# to be available, it was added in Zsh 5.3.
autoload -Uz +X add-zle-hook-widget 2>/dev/null
add-zsh-hook precmd _prompt_simpl_precmd
add-zsh-hook preexec _prompt_simpl_preexec
_prompt_simpl_state_setup
zle -N _prompt_simpl_set_cursor_style
if (( $+functions[add-zle-hook-widget] )); then
add-zle-hook-widget zle-line-finish _prompt_simpl_set_cursor_style
add-zle-hook-widget zle-keymap-select _prompt_simpl_set_cursor_style
add-zle-hook-widget zle-line-init _prompt_simpl_set_cursor_style
fi
PROMPT="%(12V.${SIMPL_VENV_COLOR}%12v ${cl}.)"
# prompt turns red if the previous command didn't exit with 0
PROMPT+="%(?.${SIMPL_PROMPT_SYMBOL_COLOR}.${SIMPL_PROMPT_SYMBOL_ERROR_COLOR})${prompt_simpl_state[prompt]}${cl} "
PROMPT2="${SIMPL_PROMPT2_SYMBOL_COLOR}${prompt_simpl_state[prompt]}${cl} "
# right prompt
if (( $SIMPL_ENABLE_RPROMPT )) && [[ -n $prompt_simpl_state[username] ]]; then
# display username and host
RPROMPT="${prompt_simpl_state[username]}"
fi
# Store prompt expansion symbols for in-place expansion via (%). For
# some reason it does not work without storing them in a variable first.
typeset -ga prompt_simpl_debug_depth
prompt_simmpl_debug_depth=('%e' '%N' '%x')
# Compare is used to check if %N equals %x. When they differ, the main
# prompt is used to allow displaying both file name and function. When
# they match, we use the secondary prompt to avoid displaying duplicate
# information.
local -A ps4_parts
ps4_parts=(
depth '%F{yellow}${(l:${(%)prompt_simpl_debug_depth[1]}::+:)}%f'
compare '${${(%)prompt_simpl_debug_depth[2]}:#${(%)prompt_simpl_debug_depth[3]}}'
main '%F{blue}${${(%)prompt_simpl_debug_depth[3]}:t}%f%F{242}:%I%f %F{242}@%f%F{blue}%N%f%F{242}:%i%f'
secondary '%F{blue}%N%f%F{242}:%i'
prompt '%F{242}>%f '
)
# Combine the parts with conditional logic. First the `:+` operator is
# used to replace `compare` either with `main` or an ampty string. Then
# the `:-` operator is used so that if `compare` becomes an empty
# string, it is replaced with `secondary`.
local ps4_symbols='${${'${ps4_parts[compare]}':+"'${ps4_parts[main]}'"}:-"'${ps4_parts[secondary]}'"}'
# Improve the debug prompt (PS4), show depth by repeating the +-sign and
# add colors to highlight essential parts like file and function name.
PROMPT4="${ps4_parts[depth]} ${ps4_symbols}${ps4_parts[prompt]}"
unset ZSH_THEME # Guard against Oh My Zsh themes overriding Simpl.
}
_prompt_simpl_setup "$@"