aboutsummaryrefslogtreecommitdiff
path: root/gitstatus/gitstatus.plugin.zsh
blob: c37af1d7090ba4453ae8d7f65ac10c0f55282534 (plain) (blame)
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
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
# Zsh bindings for gitstatus.
#
# ------------------------------------------------------------------
#
# Example: Start gitstatusd, send it a request, wait for response and print it.
#
#   source ~/gitstatus/gitstatus.plugin.zsh
#   gitstatus_start MY
#   gitstatus_query -d $PWD MY
#   typeset -m 'VCS_STATUS_*'
#
# Output:
#
#   VCS_STATUS_ACTION=''
#   VCS_STATUS_COMMIT=c000eddcff0fb38df2d0137efe24d9d2d900f209
#   VCS_STATUS_COMMITS_AHEAD=0
#   VCS_STATUS_COMMITS_BEHIND=0
#   VCS_STATUS_COMMIT_ENCODING=''
#   VCS_STATUS_COMMIT_SUMMARY='pull upstream changes from gitstatus'
#   VCS_STATUS_HAS_CONFLICTED=0
#   VCS_STATUS_HAS_STAGED=0
#   VCS_STATUS_HAS_UNSTAGED=1
#   VCS_STATUS_HAS_UNTRACKED=1
#   VCS_STATUS_INDEX_SIZE=33
#   VCS_STATUS_LOCAL_BRANCH=master
#   VCS_STATUS_NUM_ASSUME_UNCHANGED=0
#   VCS_STATUS_NUM_CONFLICTED=0
#   VCS_STATUS_NUM_STAGED=0
#   VCS_STATUS_NUM_UNSTAGED=1
#   VCS_STATUS_NUM_SKIP_WORKTREE=0
#   VCS_STATUS_NUM_STAGED_NEW=0
#   VCS_STATUS_NUM_STAGED_DELETED=0
#   VCS_STATUS_NUM_UNSTAGED_DELETED=0
#   VCS_STATUS_NUM_UNTRACKED=1
#   VCS_STATUS_PUSH_COMMITS_AHEAD=0
#   VCS_STATUS_PUSH_COMMITS_BEHIND=0
#   VCS_STATUS_PUSH_REMOTE_NAME=''
#   VCS_STATUS_PUSH_REMOTE_URL=''
#   VCS_STATUS_REMOTE_BRANCH=master
#   VCS_STATUS_REMOTE_NAME=origin
#   VCS_STATUS_REMOTE_URL=git@github.com:romkatv/powerlevel10k.git
#   VCS_STATUS_RESULT=ok-sync
#   VCS_STATUS_STASHES=0
#   VCS_STATUS_TAG=''
#   VCS_STATUS_WORKDIR=/home/romka/powerlevel10k

[[ -o 'interactive' ]] || 'return'

# Temporarily change options.
'builtin' 'local' '-a' '_gitstatus_opts'
[[ ! -o 'aliases'         ]] || _gitstatus_opts+=('aliases')
[[ ! -o 'sh_glob'         ]] || _gitstatus_opts+=('sh_glob')
[[ ! -o 'no_brace_expand' ]] || _gitstatus_opts+=('no_brace_expand')
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'

autoload -Uz add-zsh-hook        || return
zmodload zsh/datetime zsh/system || return
zmodload -F zsh/files b:zf_rm    || return

typeset -g _gitstatus_plugin_dir"${1:-}"="${${(%):-%x}:A:h}"

# Retrives status of a git repo from a directory under its working tree.
#
## Usage: gitstatus_query [OPTION]... NAME
#
#   -d STR    Directory to query. Defaults to the current directory. Has no effect if GIT_DIR
#             is set.
#   -c STR    Callback function to call once the results are available. Called only after
#             gitstatus_query returns 0 with VCS_STATUS_RESULT=tout.
#   -t FLOAT  Timeout in seconds. Negative value means infinity. Will block for at most this long.
#             If no results are available by then: if -c isn't specified, will return 1; otherwise
#             will set VCS_STATUS_RESULT=tout and return 0.
#   -p        Don't compute anything that requires reading Git index. If this option is used,
#             the following parameters will be 0: VCS_STATUS_INDEX_SIZE,
#             VCS_STATUS_{NUM,HAS}_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED}.
#
# On success sets VCS_STATUS_RESULT to one of the following values:
#
#   tout         Timed out waiting for data; will call the user-specified callback later.
#   norepo-sync  The directory isn't a git repo.
#   ok-sync      The directory is a git repo.
#
# When the callback is called, VCS_STATUS_RESULT is set to one of the following values:
#
#   norepo-async  The directory isn't a git repo.
#   ok-async      The directory is a git repo.
#
# If VCS_STATUS_RESULT is ok-sync or ok-async, additional variables are set:
#
#   VCS_STATUS_WORKDIR              Git repo working directory. Not empty.
#   VCS_STATUS_COMMIT               Commit hash that HEAD is pointing to. Either 40 hex digits or
#                                   empty if there is no HEAD (empty repo).
#   VCS_STATUS_COMMIT_ENCODING      Encoding of the HEAD's commit message. Empty value means UTF-8.
#   VCS_STATUS_COMMIT_SUMMARY       The first paragraph of the HEAD's commit message as one line.
#   VCS_STATUS_LOCAL_BRANCH         Local branch name or empty if not on a branch.
#   VCS_STATUS_REMOTE_NAME          The remote name, e.g. "upstream" or "origin".
#   VCS_STATUS_REMOTE_BRANCH        Upstream branch name. Can be empty.
#   VCS_STATUS_REMOTE_URL           Remote URL. Can be empty.
#   VCS_STATUS_ACTION               Repository state, A.K.A. action. Can be empty.
#   VCS_STATUS_INDEX_SIZE           The number of files in the index.
#   VCS_STATUS_NUM_STAGED           The number of staged changes.
#   VCS_STATUS_NUM_CONFLICTED       The number of conflicted changes.
#   VCS_STATUS_NUM_UNSTAGED         The number of unstaged changes.
#   VCS_STATUS_NUM_UNTRACKED        The number of untracked files.
#   VCS_STATUS_HAS_STAGED           1 if there are staged changes, 0 otherwise.
#   VCS_STATUS_HAS_CONFLICTED       1 if there are conflicted changes, 0 otherwise.
#   VCS_STATUS_HAS_UNSTAGED         1 if there are unstaged changes, 0 if there aren't, -1 if
#                                   unknown.
#   VCS_STATUS_NUM_STAGED_NEW       The number of staged new files. Note that renamed files
#                                   are reported as deleted plus new.
#   VCS_STATUS_NUM_STAGED_DELETED   The number of staged deleted files. Note that renamed files
#                                   are reported as deleted plus new.
#   VCS_STATUS_NUM_UNSTAGED_DELETED The number of unstaged deleted files. Note that renamed files
#                                   are reported as deleted plus new.
#   VCS_STATUS_HAS_UNTRACKED        1 if there are untracked files, 0 if there aren't, -1 if
#                                   unknown.
#   VCS_STATUS_COMMITS_AHEAD        Number of commits the current branch is ahead of upstream.
#                                   Non-negative integer.
#   VCS_STATUS_COMMITS_BEHIND       Number of commits the current branch is behind upstream.
#                                   Non-negative integer.
#   VCS_STATUS_STASHES              Number of stashes. Non-negative integer.
#   VCS_STATUS_TAG                  The last tag (in lexicographical order) that points to the same
#                                   commit as HEAD.
#   VCS_STATUS_PUSH_REMOTE_NAME     The push remote name, e.g. "upstream" or "origin".
#   VCS_STATUS_PUSH_REMOTE_URL      Push remote URL. Can be empty.
#   VCS_STATUS_PUSH_COMMITS_AHEAD   Number of commits the current branch is ahead of push remote.
#                                   Non-negative integer.
#   VCS_STATUS_PUSH_COMMITS_BEHIND  Number of commits the current branch is behind push remote.
#                                   Non-negative integer.
#   VCS_STATUS_NUM_SKIP_WORKTREE    The number of files in the index with skip-worktree bit set.
#                                   Non-negative integer.
#   VCS_STATUS_NUM_ASSUME_UNCHANGED The number of files in the index with assume-unchanged bit set.
#                                   Non-negative integer.
#
# The point of reporting -1 via VCS_STATUS_HAS_* is to allow the command to skip scanning files in
# large repos. See -m flag of gitstatus_start.
#
# gitstatus_query returns an error if gitstatus_start hasn't been called in the same shell or
# the call had failed.
#
#       !!!!! WARNING: CONCURRENT CALLS WITH THE SAME NAME ARE NOT ALLOWED !!!!!
#
# It's illegal to call gitstatus_query if the last asynchronous call with the same NAME hasn't
# completed yet. If you need to issue concurrent requests, use different NAME arguments.
function gitstatus_query"${1:-}"() {
  emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent

  local fsuf=${${(%):-%N}#gitstatus_query}

  unset VCS_STATUS_RESULT

  local opt dir callback OPTARG
  local -i no_diff OPTIND
  local -F timeout=-1
  while getopts ":d:c:t:p" opt; do
    case $opt in
      +p) no_diff=0;;
      p)  no_diff=1;;
      d)  dir=$OPTARG;;
      c)  callback=$OPTARG;;
      t)
        if [[ $OPTARG != (|+|-)<->(|.<->)(|[eE](|-|+)<->) ]]; then
          print -ru2 -- "gitstatus_query: invalid -t argument: $OPTARG"
          return 1
        fi
        timeout=OPTARG
      ;;
      \?) print -ru2 -- "gitstatus_query: invalid option: $OPTARG"           ; return 1;;
      :)  print -ru2 -- "gitstatus_query: missing required argument: $OPTARG"; return 1;;
      *)  print -ru2 -- "gitstatus_query: invalid option: $opt"              ; return 1;;
    esac
  done

  if (( OPTIND != ARGC )); then
    print -ru2 -- "gitstatus_query: exactly one positional argument is required"
    return 1
  fi

  local name=$*[OPTIND]
  if [[ $name != [[:IDENT:]]## ]]; then
    print -ru2 -- "gitstatus_query: invalid positional argument: $name"
    return 1
  fi

  (( _GITSTATUS_STATE_$name == 2 )) || return

  if [[ -z $GIT_DIR ]]; then
    [[ $dir == /* ]] || dir=${(%):-%/}/$dir
  else
    [[ $GIT_DIR == /* ]] && dir=:$GIT_DIR || dir=:${(%):-%/}/$GIT_DIR
  fi

  if [[ $dir != (|:)/* ]]; then
    typeset -g VCS_STATUS_RESULT=norepo-sync
    _gitstatus_clear$fsuf
    return 0
  fi

  local -i req_fd=${(P)${:-_GITSTATUS_REQ_FD_$name}}
  local req_id=$EPOCHREALTIME
  print -rnu $req_fd -- $req_id' '$callback$'\x1f'$dir$'\x1f'$no_diff$'\x1e' || return

  (( ++_GITSTATUS_NUM_INFLIGHT_$name ))

  if (( timeout == 0 )); then
    typeset -g VCS_STATUS_RESULT=tout
    _gitstatus_clear$fsuf
  else
    while true; do
      _gitstatus_process_response$fsuf $name $timeout $req_id || return
      [[ $VCS_STATUS_RESULT == *-async ]] || break
    done
  fi

  [[ $VCS_STATUS_RESULT != tout || -n $callback ]]
}

# If the last call to gitstatus_query timed out (VCS_STATUS_RESULT=tout), wait for the callback
# to be called. Otherwise do nothing.
#
# Usage: gitstatus_process_results [OPTION]... NAME
#
#   -t FLOAT  Timeout in seconds. Negative value means infinity. Will block for at most this long.
#
# Returns an error only when invoked with incorrect arguments and when gitstatusd isn't running or
# broken.
#
# If a callback gets called, VCS_STATUS_* parameters are set as in gitstatus_query.
# VCS_STATUS_RESULT is either norepo-async or ok-async.
function gitstatus_process_results"${1:-}"() {
  emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent

  local fsuf=${${(%):-%N}#gitstatus_process_results}

  local opt OPTARG
  local -i OPTIND
  local -F timeout=-1
  while getopts ":t:" opt; do
    case $opt in
      t)
        if [[ $OPTARG != (|+|-)<->(|.<->)(|[eE](|-|+)<->) ]]; then
          print -ru2 -- "gitstatus_process_results: invalid -t argument: $OPTARG"
          return 1
        fi
        timeout=OPTARG
      ;;
      \?) print -ru2 -- "gitstatus_process_results: invalid option: $OPTARG"           ; return 1;;
      :)  print -ru2 -- "gitstatus_process_results: missing required argument: $OPTARG"; return 1;;
      *)  print -ru2 -- "gitstatus_process_results: invalid option: $opt"              ; return 1;;
    esac
  done

  if (( OPTIND != ARGC )); then
    print -ru2 -- "gitstatus_process_results: exactly one positional argument is required"
    return 1
  fi

  local name=$*[OPTIND]
  if [[ $name != [[:IDENT:]]## ]]; then
    print -ru2 -- "gitstatus_process_results: invalid positional argument: $name"
    return 1
  fi

  (( _GITSTATUS_STATE_$name == 2 )) || return

  while (( _GITSTATUS_NUM_INFLIGHT_$name )); do
    _gitstatus_process_response$fsuf $name $timeout '' || return
    [[ $VCS_STATUS_RESULT == *-async ]] || break
  done

  return 0
}

function _gitstatus_clear"${1:-}"() {
  unset VCS_STATUS_{WORKDIR,COMMIT,LOCAL_BRANCH,REMOTE_BRANCH,REMOTE_NAME,REMOTE_URL,ACTION,INDEX_SIZE,NUM_STAGED,NUM_UNSTAGED,NUM_CONFLICTED,NUM_UNTRACKED,HAS_STAGED,HAS_UNSTAGED,HAS_CONFLICTED,HAS_UNTRACKED,COMMITS_AHEAD,COMMITS_BEHIND,STASHES,TAG,NUM_UNSTAGED_DELETED,NUM_STAGED_NEW,NUM_STAGED_DELETED,PUSH_REMOTE_NAME,PUSH_REMOTE_URL,PUSH_COMMITS_AHEAD,PUSH_COMMITS_BEHIND,NUM_SKIP_WORKTREE,NUM_ASSUME_UNCHANGED}
}

function _gitstatus_process_response"${1:-}"() {
  local name=$1 timeout req_id=$3 buf
  local -i resp_fd=_GITSTATUS_RESP_FD_$name
  local -i dirty_max_index_size=_GITSTATUS_DIRTY_MAX_INDEX_SIZE_$name

  (( $2 >= 0 )) && timeout=-t$2 && [[ -t $resp_fd ]]
  sysread $timeout -i $resp_fd 'buf[$#buf+1]' || {
    if (( $? == 4 )); then
      if [[ -n $req_id ]]; then
        typeset -g VCS_STATUS_RESULT=tout
        _gitstatus_clear$fsuf
      fi
      return 0
    else
      gitstatus_stop$fsuf $name
      return 1
    fi
  }
  while [[ $buf != *$'\x1e' ]]; do
    if ! sysread -i $resp_fd 'buf[$#buf+1]'; then
      gitstatus_stop$fsuf $name
      return 1
    fi
  done

  local s
  for s in ${(ps:\x1e:)buf}; do
    local -a resp=("${(@ps:\x1f:)s}")
    if (( resp[2] )); then
      if [[ $resp[1] == $req_id' '* ]]; then
        typeset -g VCS_STATUS_RESULT=ok-sync
      else
        typeset -g VCS_STATUS_RESULT=ok-async
      fi
      for VCS_STATUS_WORKDIR              \
          VCS_STATUS_COMMIT               \
          VCS_STATUS_LOCAL_BRANCH         \
          VCS_STATUS_REMOTE_BRANCH        \
          VCS_STATUS_REMOTE_NAME          \
          VCS_STATUS_REMOTE_URL           \
          VCS_STATUS_ACTION               \
          VCS_STATUS_INDEX_SIZE           \
          VCS_STATUS_NUM_STAGED           \
          VCS_STATUS_NUM_UNSTAGED         \
          VCS_STATUS_NUM_CONFLICTED       \
          VCS_STATUS_NUM_UNTRACKED        \
          VCS_STATUS_COMMITS_AHEAD        \
          VCS_STATUS_COMMITS_BEHIND       \
          VCS_STATUS_STASHES              \
          VCS_STATUS_TAG                  \
          VCS_STATUS_NUM_UNSTAGED_DELETED \
          VCS_STATUS_NUM_STAGED_NEW       \
          VCS_STATUS_NUM_STAGED_DELETED   \
          VCS_STATUS_PUSH_REMOTE_NAME     \
          VCS_STATUS_PUSH_REMOTE_URL      \
          VCS_STATUS_PUSH_COMMITS_AHEAD   \
          VCS_STATUS_PUSH_COMMITS_BEHIND  \
          VCS_STATUS_NUM_SKIP_WORKTREE    \
          VCS_STATUS_NUM_ASSUME_UNCHANGED \
          VCS_STATUS_COMMIT_ENCODING      \
          VCS_STATUS_COMMIT_SUMMARY in "${(@)resp[3,29]}"; do
      done
      typeset -gi VCS_STATUS_{INDEX_SIZE,NUM_STAGED,NUM_UNSTAGED,NUM_CONFLICTED,NUM_UNTRACKED,COMMITS_AHEAD,COMMITS_BEHIND,STASHES,NUM_UNSTAGED_DELETED,NUM_STAGED_NEW,NUM_STAGED_DELETED,PUSH_COMMITS_AHEAD,PUSH_COMMITS_BEHIND,NUM_SKIP_WORKTREE,NUM_ASSUME_UNCHANGED}
      typeset -gi VCS_STATUS_HAS_STAGED=$((VCS_STATUS_NUM_STAGED > 0))
      if (( dirty_max_index_size >= 0 && VCS_STATUS_INDEX_SIZE > dirty_max_index_size )); then
        typeset -gi                    \
          VCS_STATUS_HAS_UNSTAGED=-1   \
          VCS_STATUS_HAS_CONFLICTED=-1 \
          VCS_STATUS_HAS_UNTRACKED=-1
      else
        typeset -gi                                                    \
          VCS_STATUS_HAS_UNSTAGED=$((VCS_STATUS_NUM_UNSTAGED > 0))     \
          VCS_STATUS_HAS_CONFLICTED=$((VCS_STATUS_NUM_CONFLICTED > 0)) \
          VCS_STATUS_HAS_UNTRACKED=$((VCS_STATUS_NUM_UNTRACKED > 0))
      fi
    else
      if [[ $resp[1] == $req_id' '* ]]; then
        typeset -g VCS_STATUS_RESULT=norepo-sync
      else
        typeset -g VCS_STATUS_RESULT=norepo-async
      fi
      _gitstatus_clear$fsuf
    fi
    (( --_GITSTATUS_NUM_INFLIGHT_$name ))
    [[ $VCS_STATUS_RESULT == *-async ]] && emulate zsh -c "${resp[1]#* }"
  done

  return 0
}

function _gitstatus_daemon"${1:-}"() {
  local -i pipe_fd
  exec 0<&- {pipe_fd}>&1 1>>$daemon_log 2>&1 || return
  local pgid=$sysparams[pid]
  [[ $pgid == <1-> ]] || return
  builtin cd -q /     || return

  {
    {
      trap '' PIPE

      local uname_sm
      uname_sm="${${(L)$(command uname -sm)}//ı/i}" || return
      [[ $uname_sm == [^' ']##' '[^' ']## ]]        || return
      local uname_s=${uname_sm% *}
      local uname_m=${uname_sm#* }

      if [[ $GITSTATUS_NUM_THREADS == <1-> ]]; then
        args+=(-t $GITSTATUS_NUM_THREADS)
      else
        local cpus
        if (( ! $+commands[sysctl] )) || [[ $uname_s == linux ]] ||
            ! cpus="$(command sysctl -n hw.ncpu)"; then
          if (( ! $+commands[getconf] )) || ! cpus="$(command getconf _NPROCESSORS_ONLN)"; then
            cpus=8
          fi
        fi
        args+=(-t $((cpus > 16 ? 32 : cpus > 0 ? 2 * cpus : 16)))
      fi

      command mkfifo -- $file_prefix.fifo   || return
      print -rnu $pipe_fd -- ${(l:20:)pgid} || return
      exec <$file_prefix.fifo               || return
      zf_rm -- $file_prefix.fifo            || return

      local _gitstatus_zsh_daemon _gitstatus_zsh_version _gitstatus_zsh_downloaded

      function _gitstatus_set_daemon$fsuf() {
        _gitstatus_zsh_daemon="$1"
        _gitstatus_zsh_version="$2"
        _gitstatus_zsh_downloaded="$3"
      }

      local gitstatus_plugin_dir_var=_gitstatus_plugin_dir$fsuf
      local gitstatus_plugin_dir=${(P)gitstatus_plugin_dir_var}
      builtin set -- -d $gitstatus_plugin_dir -s $uname_s -m $uname_m \
        -p "printf '\\001' >&$pipe_fd" -e $pipe_fd -- _gitstatus_set_daemon$fsuf
      [[ ${GITSTATUS_AUTO_INSTALL:-1} == (|-|+)<1-> ]] || builtin set -- -n "$@"
      builtin source $gitstatus_plugin_dir/install     || return
      [[ -n $_gitstatus_zsh_daemon ]]                  || return
      [[ -n $_gitstatus_zsh_version ]]                 || return
      [[ $_gitstatus_zsh_downloaded == [01] ]]         || return

      if (( UID == EUID )); then
        local home=~
      else
        local user
        user="$(command id -un)" || return
        local home=${userdirs[$user]}
        [[ -n $home ]] || return
      fi

      if [[ -x $_gitstatus_zsh_daemon ]]; then
        HOME=$home $_gitstatus_zsh_daemon -G $_gitstatus_zsh_version "${(@)args}" >&$pipe_fd
        local -i ret=$?
        [[ $ret == (0|129|130|131|137|141|143|159) ]] && return ret
      fi

      (( ! _gitstatus_zsh_downloaded ))                || return
      [[ ${GITSTATUS_AUTO_INSTALL:-1} == (|-|+)<1-> ]] || return
      [[ $_gitstatus_zsh_daemon == \
         ${GITSTATUS_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/gitstatus}/* ]] || return

      builtin set -- -f "$@"
      _gitstatus_zsh_daemon=
      _gitstatus_zsh_version=
      _gitstatus_zsh_downloaded=
      builtin source $gitstatus_plugin_dir/install || return
      [[ -n $_gitstatus_zsh_daemon ]]              || return
      [[ -n $_gitstatus_zsh_version ]]             || return
      [[ $_gitstatus_zsh_downloaded == 1 ]]        || return

      HOME=$home $_gitstatus_zsh_daemon -G $_gitstatus_zsh_version "${(@)args}" >&$pipe_fd
    } always {
      local -i ret=$?
      zf_rm -f -- $file_prefix.lock $file_prefix.fifo
      kill -- -$pgid
    }
  } &!

  (( lock_fd == -1 )) && return

  {
    if zsystem flock -- $file_prefix.lock && [[ -e $file_prefix.lock ]]; then
      zf_rm -f -- $file_prefix.lock $file_prefix.fifo
      kill -- -$pgid
    fi
  } &!
}

# Starts gitstatusd in the background. Does nothing and succeeds if gitstatusd is already running.
#
# Usage: gitstatus_start [OPTION]... NAME
#
#   -t FLOAT  Fail the self-check on initialization if not getting a response from gitstatusd for
#             this this many seconds. Defaults to 5.
#
#   -s INT    Report at most this many staged changes; negative value means infinity.
#             Defaults to 1.
#
#   -u INT    Report at most this many unstaged changes; negative value means infinity.
#             Defaults to 1.
#
#   -c INT    Report at most this many conflicted changes; negative value means infinity.
#             Defaults to 1.
#
#   -d INT    Report at most this many untracked files; negative value means infinity.
#             Defaults to 1.
#
#   -m INT    Report -1 unstaged, untracked and conflicted if there are more than this many
#             files in the index. Negative value means infinity. Defaults to -1.
#
#   -e        Count files within untracked directories like `git status --untracked-files`.
#
#   -U        Unless this option is specified, report zero untracked files for repositories
#             with status.showUntrackedFiles = false.
#
#   -W        Unless this option is specified, report zero untracked files for repositories
#             with bash.showUntrackedFiles = false.
#
#   -D        Unless this option is specified, report zero staged, unstaged and conflicted
#             changes for repositories with bash.showDirtyState = false.
function gitstatus_start"${1:-}"() {
  emulate -L zsh -o no_aliases -o no_bg_nice -o extended_glob -o typeset_silent || return
  print -rnu2 || return

  local fsuf=${${(%):-%N}#gitstatus_start}

  local opt OPTARG
  local -i OPTIND
  local -F timeout=5
  local -i async=0
  local -a args=()
  local -i dirty_max_index_size=-1

  while getopts ":t:s:u:c:d:m:eaUWD" opt; do
    case $opt in
      a)  async=1;;
      +a) async=0;;
      t)
        if [[ $OPTARG != (|+)<->(|.<->)(|[eE](|-|+)<->) ]] || (( ${timeout::=OPTARG} <= 0 )); then
          print -ru2 -- "gitstatus_start: invalid -t argument: $OPTARG"
          return 1
        fi
      ;;
      s|u|c|d|m)
        if [[ $OPTARG != (|-|+)<-> ]]; then
          print -ru2 -- "gitstatus_start: invalid -$opt argument: $OPTARG"
          return 1
        fi
        args+=(-$opt $OPTARG)
        [[ $opt == m ]] && dirty_max_index_size=OPTARG
      ;;
      e|U|W|D)    args+=-$opt;;
      +(e|U|W|D)) args=(${(@)args:#-$opt});;
      \?) print -ru2 -- "gitstatus_start: invalid option: $OPTARG"           ; return 1;;
      :)  print -ru2 -- "gitstatus_start: missing required argument: $OPTARG"; return 1;;
      *)  print -ru2 -- "gitstatus_start: invalid option: $opt"              ; return 1;;
    esac
  done

  if (( OPTIND != ARGC )); then
    print -ru2 -- "gitstatus_start: exactly one positional argument is required"
    return 1
  fi

  local name=$*[OPTIND]
  if [[ $name != [[:IDENT:]]## ]]; then
    print -ru2 -- "gitstatus_start: invalid positional argument: $name"
    return 1
  fi

  local -i lock_fd resp_fd stderr_fd
  local file_prefix xtrace=/dev/null daemon_log=/dev/null culprit

  {
    if (( _GITSTATUS_STATE_$name )); then
      (( async )) && return
      (( _GITSTATUS_STATE_$name == 2 )) && return
      lock_fd=_GITSTATUS_LOCK_FD_$name
      resp_fd=_GITSTATUS_RESP_FD_$name
      xtrace=${(P)${:-GITSTATUS_XTRACE_$name}}
      daemon_log=${(P)${:-GITSTATUS_DAEMON_LOG_$name}}
      file_prefix=${(P)${:-_GITSTATUS_FILE_PREFIX_$name}}
    else
      typeset -gi _GITSTATUS_START_COUNTER
      local log_level=$GITSTATUS_LOG_LEVEL
      local file_prefix=${${TMPDIR:-/tmp}:A}/gitstatus.$name.$EUID
      file_prefix+=.$sysparams[pid].$EPOCHSECONDS.$((++_GITSTATUS_START_COUNTER))
      (( GITSTATUS_ENABLE_LOGGING )) && : ${log_level:=INFO}
      if [[ -n $log_level ]]; then
        xtrace=$file_prefix.xtrace.log
        daemon_log=$file_prefix.daemon.log
      fi
      args+=(-v ${log_level:-FATAL})
      typeset -g GITSTATUS_XTRACE_$name=$xtrace
      typeset -g GITSTATUS_DAEMON_LOG_$name=$daemon_log
      typeset -g _GITSTATUS_FILE_PREFIX_$name=$file_prefix
      typeset -gi _GITSTATUS_CLIENT_PID_$name="sysparams[pid]"
      typeset -gi _GITSTATUS_DIRTY_MAX_INDEX_SIZE_$name=dirty_max_index_size
    fi

    () {
      if [[ $xtrace != /dev/null && -o no_xtrace ]]; then
        exec {stderr_fd}>&2 || return
        exec 2>>$xtrace     || return
        setopt xtrace
      fi

      setopt monitor || return

      if (( ! _GITSTATUS_STATE_$name )); then
        if [[ -r /proc/version && "$(</proc/version)" == *Microsoft* ]]; then
          lock_fd=-1
        else
          print -rn >$file_prefix.lock               || return
          zsystem flock -f lock_fd $file_prefix.lock || return
          [[ $lock_fd == <1-> ]]                     || return
        fi

        typeset -gi _GITSTATUS_LOCK_FD_$name=lock_fd

        if [[ $OSTYPE == cygwin* && -d /proc/self/fd ]]; then
          # Work around bugs in Cygwin 32-bit.
          #
          # This hangs:
          #
          #   emulate -L zsh
          #   () { exec {fd}< $1 } <(:)
          #   =true  # hangs here
          #
          # This hangs:
          #
          #   sysopen -r -u fd <(:)
          local -i fd
          exec {fd}< <(_gitstatus_daemon$fsuf)                       || return
          {
            [[ -r /proc/self/fd/$fd ]]                               || return
            sysopen -r -o cloexec -u resp_fd /proc/self/fd/$fd       || return
          } always {
            exec {fd} >&-                                            || return
          }
        else
          sysopen -r -o cloexec -u resp_fd <(_gitstatus_daemon$fsuf) || return
        fi

        typeset -gi GITSTATUS_DAEMON_PID_$name="${sysparams[procsubstpid]:--1}"

        [[ $resp_fd == <1-> ]] || return
        typeset -gi _GITSTATUS_RESP_FD_$name=resp_fd
        typeset -gi _GITSTATUS_STATE_$name=1
      fi

      if (( ! async )); then
        (( _GITSTATUS_CLIENT_PID_$name == sysparams[pid] )) || return

        local pgid
        while (( $#pgid < 20 )); do
          [[ -t $resp_fd ]]
          sysread -s $((20 - $#pgid)) -t $timeout -i $resp_fd 'pgid[$#pgid+1]' || return
        done
        [[ $pgid == ' '#<1-> ]] || return
        typeset -gi GITSTATUS_DAEMON_PID_$name=pgid

        sysopen -w -o cloexec -u req_fd -- $file_prefix.fifo || return
        [[ $req_fd == <1-> ]]                                || return
        typeset -gi _GITSTATUS_REQ_FD_$name=req_fd

        print -nru $req_fd -- $'}hello\x1f\x1e' || return
        local expected=$'}hello\x1f0\x1e' actual
        if (( $+functions[p10k] )) && [[ ! -t 1 && ! -t 0 ]]; then
          local -F deadline='EPOCHREALTIME + 4'
        else
          local -F deadline='1'
        fi
        while true; do
          [[ -t $resp_fd ]]
          sysread -s 1 -t $timeout -i $resp_fd actual || return
          [[ $expected == $actual* ]] && break
          if [[ $actual != $'\1' ]]; then
            [[ -t $resp_fd ]]
            while sysread -t $timeout -i $resp_fd 'actual[$#actual+1]'; do
              [[ -t $resp_fd ]]
            done
            culprit=$actual
            return 1
          fi
          (( EPOCHREALTIME < deadline )) && continue
          if (( deadline > 0 )); then
            deadline=0
            if (( stderr_fd )); then
              unsetopt xtrace
              exec 2>&$stderr_fd {stderr_fd}>&-
              stderr_fd=0
            fi
            if (( $+functions[p10k] )); then
              p10k clear-instant-prompt || return
            fi
            if [[ $name == POWERLEVEL9K ]]; then
              local label=powerlevel10k
            else
              local label=gitstatus
            fi
            if [[ -t 2 ]]; then
              local spinner=($'\b%3F-%f' $'\b%3F\\%f' $'\b%3F|%f' $'\b%3F/%f')
              print -Prnu2 -- "[%3F$label%f] fetching %2Fgitstatusd%f ..  "
            else
              local spinner=('.')
              print -rnu2 -- "[$label] fetching gitstatusd .."
            fi
          fi
          print -Prnu2 -- $spinner[1]
          spinner=($spinner[2,-1] $spinner[1])
        done

        if (( deadline == 0 )); then
          if [[ -t 2 ]]; then
            print -Pru2 -- $'\b[%2Fok%f]'
          else
            print -ru2 -- ' [ok]'
          fi
          if [[ $xtrace != /dev/null && -o no_xtrace ]]; then
            exec {stderr_fd}>&2 || return
            exec 2>>$xtrace     || return
            setopt xtrace
          fi
        fi

        while (( $#actual < $#expected )); do
          [[ -t $resp_fd ]]
          sysread -s $(($#expected - $#actual)) -t $timeout -i $resp_fd 'actual[$#actual+1]' || return
        done
        [[ $actual == $expected ]] || return

        function _gitstatus_process_response_$name-$fsuf() {
          emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent
          local pair=${${(%):-%N}#_gitstatus_process_response_}
          local name=${pair%%-*}
          local fsuf=${pair#*-}
          [[ $name == POWERLEVEL9K && $fsuf == _p9k_ ]] && eval $__p9k_intro_base
          if (( ARGC == 1 )); then
            _gitstatus_process_response$fsuf $name 0 ''
          else
            gitstatus_stop$fsuf $name
          fi
        }
        if ! zle -F $resp_fd _gitstatus_process_response_$name-$fsuf; then
          unfunction _gitstatus_process_response_$name-$fsuf
          return 1
        fi

        function _gitstatus_cleanup_$name-$fsuf() {
          emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent
          local pair=${${(%):-%N}#_gitstatus_cleanup_}
          local name=${pair%%-*}
          local fsuf=${pair#*-}
          (( _GITSTATUS_CLIENT_PID_$name == sysparams[pid] )) || return
          gitstatus_stop$fsuf $name
        }
        if ! add-zsh-hook zshexit _gitstatus_cleanup_$name-$fsuf; then
          unfunction _gitstatus_cleanup_$name-$fsuf
          return 1
        fi

        if (( lock_fd != -1 )); then
          zf_rm -- $file_prefix.lock || return
          zsystem flock -u $lock_fd  || return
        fi
        unset _GITSTATUS_LOCK_FD_$name

        typeset -gi _GITSTATUS_STATE_$name=2
      fi
    }
  } always {
    local -i err=$?
    (( stderr_fd )) && exec 2>&$stderr_fd {stderr_fd}>&-
    (( err == 0  )) && return

    gitstatus_stop$fsuf $name

    setopt prompt_percent no_prompt_subst no_prompt_bang
    (( $+functions[p10k] )) && p10k clear-instant-prompt
    print -ru2  -- ''
    print -Pru2 -- '[%F{red}ERROR%f]: gitstatus failed to initialize.'
    print -ru2  -- ''
    if [[ -n $culprit ]]; then
      print -ru2 -- $culprit
      return err
    fi
    if [[ -s $xtrace ]]; then
      print -ru2  -- ''
      print -Pru2 -- "  Zsh log (%U${xtrace//\%/%%}%u):"
      print -Pru2 -- '%F{yellow}'
      print -lru2 -- "${(@)${(@f)$(<$xtrace)}/#/    }"
      print -Pnru2 -- '%f'
    fi
    if [[ -s $daemon_log ]]; then
      print -ru2   -- ''
      print -Pru2  -- "  Daemon log (%U${daemon_log//\%/%%}%u):"
      print -Pru2  -- '%F{yellow}'
      print -lru2  -- "${(@)${(@f)$(<$daemon_log)}/#/    }"
      print -Pnru2 -- '%f'
    fi
    if [[ $GITSTATUS_LOG_LEVEL == DEBUG ]]; then
      print -ru2   -- ''
      print -ru2   -- '  System information:'
      print -Pru2  -- '%F{yellow}'
      print -ru2   -- "    zsh:      $ZSH_VERSION"
      print -ru2   -- "    uname -a: $(command uname -a)"
      print -Pru2  -- '%f'
      print -ru2   -- '  If you need help, open an issue and attach this whole error message to it:'
      print -ru2   -- ''
      print -Pru2  -- '    %Uhttps://github.com/romkatv/gitstatus/issues/new%u'
    else
      print -ru2   -- ''
      local home=~
      local zshrc=${${${(q)${ZDOTDIR:-~}}/#${(q)home}/'~'}//\%/%%}/.zshrc
      print -Pru2   -- "  Add the following parameter to %U$zshrc%u for extra diagnostics on error:"
      print -ru2   -- ''
      print -Pru2  -- '    %BGITSTATUS_LOG_LEVEL=DEBUG%b'
      print -ru2   -- ''
      print -ru2   -- '  Restart Zsh to retry gitstatus initialization:'
      print -ru2   -- ''
      print -Pru2   -- '    %F{green}%Uexec%u zsh%f'
    fi
  }
}

# Stops gitstatusd if it's running.
#
# Usage: gitstatus_stop NAME.
function gitstatus_stop"${1:-}"() {
  emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent

  local fsuf=${${(%):-%N}#gitstatus_stop}

  if (( ARGC != 1 )); then
    print -ru2 -- "gitstatus_stop: exactly one positional argument is required"
    return 1
  fi

  local name=$1
  if [[ $name != [[:IDENT:]]## ]]; then
    print -ru2 -- "gitstatus_stop: invalid positional argument: $name"
    return 1
  fi

  local state_var=_GITSTATUS_STATE_$name
  local req_fd_var=_GITSTATUS_REQ_FD_$name
  local resp_fd_var=_GITSTATUS_RESP_FD_$name
  local lock_fd_var=_GITSTATUS_LOCK_FD_$name
  local client_pid_var=_GITSTATUS_CLIENT_PID_$name
  local daemon_pid_var=GITSTATUS_DAEMON_PID_$name
  local inflight_var=_GITSTATUS_NUM_INFLIGHT_$name
  local file_prefix_var=_GITSTATUS_FILE_PREFIX_$name
  local dirty_max_index_size_var=_GITSTATUS_DIRTY_MAX_INDEX_SIZE_$name

  local req_fd=${(P)req_fd_var}
  local resp_fd=${(P)resp_fd_var}
  local lock_fd=${(P)lock_fd_var}
  local daemon_pid=${(P)daemon_pid_var}
  local file_prefix=${(P)file_prefix_var}

  local cleanup=_gitstatus_cleanup_$name-$fsuf
  local process=_gitstatus_process_response_$name-$fsuf

  if (( $+functions[$cleanup] )); then
    add-zsh-hook -d zshexit $cleanup
    unfunction -- $cleanup
  fi

  if (( $+functions[$process] )); then
    [[ -n $resp_fd ]] && zle -F $resp_fd
    unfunction -- $process
  fi

  [[ $daemon_pid  == <1-> ]] && kill -- -$daemon_pid 2>/dev/null
  [[ $file_prefix == /*   ]] && zf_rm -f -- $file_prefix.lock $file_prefix.fifo
  [[ $lock_fd     == <1-> ]] && zsystem flock -u $lock_fd
  [[ $req_fd      == <1-> ]] && exec {req_fd}>&-
  [[ $resp_fd     == <1-> ]] && exec {resp_fd}>&-

  unset $state_var $req_fd_var $lock_fd_var $resp_fd_var $client_pid_var $daemon_pid_var
  unset $inflight_var $file_prefix_var $dirty_max_index_size_var

  unset VCS_STATUS_RESULT
  _gitstatus_clear$fsuf
}

# Usage: gitstatus_check NAME.
#
# Returns 0 if and only if `gitstatus_start NAME` has succeeded previously.
# If it returns non-zero, gitstatus_query NAME is guaranteed to return non-zero.
function gitstatus_check"${1:-}"() {
  emulate -L zsh -o no_aliases -o extended_glob -o typeset_silent

  local fsuf=${${(%):-%N}#gitstatus_check}

  if (( ARGC != 1 )); then
    print -ru2 -- "gitstatus_check: exactly one positional argument is required"
    return 1
  fi

  local name=$1
  if [[ $name != [[:IDENT:]]## ]]; then
    print -ru2 -- "gitstatus_check: invalid positional argument: $name"
    return 1
  fi

  (( _GITSTATUS_STATE_$name == 2 ))
}

(( ${#_gitstatus_opts} )) && setopt ${_gitstatus_opts[@]}
'builtin' 'unset' '_gitstatus_opts'