Skip to content

Commit e37743c

Browse files
committed
Add async git status to avoid blocking prompt
The prompt now fetches git status asynchronously using zsh's zle -F mechanism. This provides p10k-like responsiveness: 1. Prompt appears immediately with directory only 2. Git info (branch, staged, unstaged, untracked) loads in background 3. Prompt updates automatically when git status completes Uses native zsh features (exec fd redirection, zle -F callbacks) with no external dependencies.
1 parent 3625c31 commit e37743c

File tree

1 file changed

+104
-26
lines changed

1 file changed

+104
-26
lines changed

dot_prompt.zsh

Lines changed: 104 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,106 @@
11
# Simple, reliable prompt similar to p10k lean style
22
# Shows: directory, git info, command execution time, exit status
3+
# Git status is fetched asynchronously to avoid blocking
34

45
# Enable colors
56
autoload -U colors && colors
67

7-
# Enable vcs_info for git status
8-
autoload -Uz vcs_info
9-
zstyle ':vcs_info:*' enable git
10-
zstyle ':vcs_info:*' check-for-changes true
11-
zstyle ':vcs_info:*' stagedstr '%F{green}+'
12-
zstyle ':vcs_info:*' unstagedstr '%F{yellow}!'
13-
zstyle ':vcs_info:git:*' formats '%F{cyan}%b%f%c%u'
14-
zstyle ':vcs_info:git:*' actionformats '%F{cyan}%b%f|%F{red}%a%f%c%u'
8+
# ============================================================================
9+
# Async git status
10+
# ============================================================================
1511

16-
# Track command execution time
12+
# State variables
13+
_prompt_git_info=""
1714
_prompt_exec_start=0
1815
_prompt_exec_time=0
16+
_prompt_exit_code=0
17+
_prompt_async_fd=""
18+
_prompt_async_pid=""
19+
20+
# Get git status synchronously (called in background)
21+
_prompt_git_status() {
22+
cd -q "$1" 2>/dev/null || return
23+
24+
# Check if we're in a git repo
25+
local branch
26+
branch=$(git symbolic-ref --short HEAD 2>/dev/null) || \
27+
branch=$(git rev-parse --short HEAD 2>/dev/null) || return
28+
29+
local result="%F{cyan}${branch}%f"
30+
31+
# Check for staged changes
32+
if ! git diff --cached --quiet 2>/dev/null; then
33+
result+="%F{green}+%f"
34+
fi
35+
36+
# Check for unstaged changes
37+
if ! git diff --quiet 2>/dev/null; then
38+
result+="%F{yellow}!%f"
39+
fi
40+
41+
# Check for untracked files
42+
if [[ -n $(git ls-files --others --exclude-standard 2>/dev/null | head -1) ]]; then
43+
result+="%F{blue}?%f"
44+
fi
45+
46+
echo " ${result}"
47+
}
48+
49+
# Callback when async job completes
50+
_prompt_async_callback() {
51+
local fd="$1"
52+
53+
# Read result from file descriptor
54+
_prompt_git_info=""
55+
if [[ -n "$fd" ]] && read -r -u "$fd" _prompt_git_info 2>/dev/null; then
56+
: # Successfully read
57+
fi
58+
59+
# Clean up file descriptor
60+
if [[ -n "$_prompt_async_fd" ]]; then
61+
zle -F "$_prompt_async_fd" 2>/dev/null
62+
exec {_prompt_async_fd}<&- 2>/dev/null
63+
_prompt_async_fd=""
64+
fi
65+
66+
_prompt_async_pid=""
67+
68+
# Redraw prompt with new git info
69+
zle && zle reset-prompt
70+
}
71+
72+
# Start async git status fetch
73+
_prompt_async_start() {
74+
# Kill any existing async job
75+
if [[ -n "$_prompt_async_pid" ]]; then
76+
kill "$_prompt_async_pid" 2>/dev/null
77+
_prompt_async_pid=""
78+
fi
79+
80+
# Clean up old file descriptor
81+
if [[ -n "$_prompt_async_fd" ]]; then
82+
zle -F "$_prompt_async_fd" 2>/dev/null
83+
exec {_prompt_async_fd}<&- 2>/dev/null
84+
_prompt_async_fd=""
85+
fi
86+
87+
# Clear git info while loading
88+
_prompt_git_info=""
89+
90+
# Quick check: are we in a git repo at all?
91+
git rev-parse --is-inside-work-tree &>/dev/null || return
92+
93+
# Start background job and capture output via fd
94+
exec {_prompt_async_fd}< <(_prompt_git_status "$PWD")
95+
_prompt_async_pid=$!
96+
97+
# Register callback for when data is available
98+
zle -F "$_prompt_async_fd" _prompt_async_callback
99+
}
100+
101+
# ============================================================================
102+
# Prompt hooks
103+
# ============================================================================
19104

20105
preexec() {
21106
_prompt_exec_start=$EPOCHREALTIME
@@ -35,10 +120,14 @@ precmd() {
35120
# Store exit code for prompt
36121
_prompt_exit_code=$exit_code
37122

38-
# Update vcs_info
39-
vcs_info
123+
# Start async git status fetch
124+
_prompt_async_start
40125
}
41126

127+
# ============================================================================
128+
# Prompt segments
129+
# ============================================================================
130+
42131
# Format execution time (only show if > 3 seconds)
43132
_prompt_format_time() {
44133
(( _prompt_exec_time < 3 )) && return
@@ -67,29 +156,18 @@ _prompt_char() {
67156
fi
68157
}
69158

70-
# Git info with untracked files indicator
71-
_prompt_git() {
72-
[[ -z "${vcs_info_msg_0_}" ]] && return
73-
74-
local git_info="${vcs_info_msg_0_}"
75-
76-
# Check for untracked files
77-
if command git status --porcelain 2>/dev/null | grep -q '^??'; then
78-
git_info+='%F{blue}?%f'
79-
fi
80-
81-
echo " ${git_info}"
82-
}
83-
84159
# Right prompt with time
85160
_prompt_time() {
86161
echo "%F{8}%T%f"
87162
}
88163

164+
# ============================================================================
89165
# Set prompts
166+
# ============================================================================
167+
90168
setopt PROMPT_SUBST
91169

92-
PROMPT='%F{blue}%~%f$(_prompt_git)
170+
PROMPT='%F{blue}%~%f${_prompt_git_info}
93171
$(_prompt_char) '
94172

95173
RPROMPT='$(_prompt_format_time)$(_prompt_time)'

0 commit comments

Comments
 (0)