-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathzsh-github-copilot.plugin.zsh
141 lines (123 loc) · 3.53 KB
/
zsh-github-copilot.plugin.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
typeset -g GH_COPILOT_RESULT_FILE
typeset -g RESET
typeset -g RED
typeset -g GREEN
GH_COPILOT_RESULT_FILE="${GH_COPILOT_RESULT_FILE:-/tmp/zsh_gh_copilot_result}"
if type tput >/dev/null; then
RESET="$(tput sgr0)"
RED="$(tput setaf 1)"
GREEN="$(tput setaf 2)"
else
RESET=""
RED=""
GREEN=""
fi
_echo_exit() {
printf "%s%s%s" "$RED" "$@" "$RESET" >&2
return 1
}
if [[ -z $ZSH_GH_COPILOT_NO_CHECK ]]; then
type tput >/dev/null || _echo_exit "zsh-github-copilot: tput not found."
type gh >/dev/null || _echo_exit "zsh-github-copilot: gh not found."
gh extension list | grep -q "gh[- ]copilot" || _echo_exit "zsh-github-copilot: gh copilot extension not found."
fi
_gh_copilot() {
# run gh copilot without interactivity
echo "" | gh copilot "$@" 2>/dev/null
}
_spinner() {
local pid=$1
local delay=0.1
local spin='⣾⣽⣻⢿⡿⣟⣯⣷'
cleanup() {
# shellcheck disable=SC2317
kill "$pid"
# shellcheck disable=SC2317
tput cnorm
}
trap cleanup SIGINT
i=0
# while the copilot process is running
tput civis
while kill -0 "$pid" 2>/dev/null; do
i=$(((i + 1) % ${#spin}))
printf " %s%s%s" "${RED}" ${spin:$i:1} "${RESET}"
sleep "$delay"
printf "\b\b\b"
done
printf " \b\b\b"
tput cnorm
trap - SIGINT
}
_gh_copilot_spinner() {
# run gh copilot in the background and show a spinner
read -r < <(
_gh_copilot "$@" >|"$GH_COPILOT_RESULT_FILE" &
echo $!
)
_spinner "$REPLY" >&2
cat "$GH_COPILOT_RESULT_FILE"
}
_gh_copilot_explain() {
local result
local pattern
# the explanation starts with a bullet point
pattern='^\s*•'
result="$(
_gh_copilot_spinner explain "$@" |
grep "$pattern"
)"
__trim_string "$result"
}
_gh_copilot_suggest() {
local result
local pattern
# the suggestions start with 4 spaces
pattern='^ '
result="$(
_gh_copilot_spinner suggest -t shell "$@" |
grep "$pattern"
)"
__trim_string "$result"
}
__trim_string() {
# reomve leading and trailing whitespaces
# from https://github.com/dylanaraps/pure-bash-bible?tab=readme-ov-file#trim-leading-and-trailing-white-space-from-string
# Usage: trim_string " example string "
: "${1#"${1%%[![:space:]]*}"}"
: "${_%"${_##*[![:space:]]}"}"
printf '%s\n' "$_"
}
_prompt_msg() {
# print a message to the prompt
printf "\n%s%s%s\n\n" "$GREEN" "$@" "$RESET"
# this isn't great because it might not work with multiline prompts
zle reset-prompt
}
zsh_gh_copilot_suggest() {
# based on https://github.com/stefanheule/zsh-llm-suggestions/blob/master/zsh-llm-suggestions.zsh#L65
# check if the buffer is empty
[ -z "$BUFFER" ] && return
zle end-of-line
local result
# place the query in history
print -s "$BUFFER"
result="$(_gh_copilot_suggest "$BUFFER")"
[ -z "$result" ] && _prompt_msg "No suggestion found" && return
zle reset-prompt
# replace the current buffer with the result
BUFFER="${result}"
# shellcheck disable=SC2034
CURSOR=${#BUFFER}
}
zsh_gh_copilot_explain() {
# based on https://github.com/stefanheule/zsh-llm-suggestions/blob/master/zsh-llm-suggestions.zsh#L71
# check if the buffer is empty
[ -z "$BUFFER" ] && return
zle end-of-line
local result
result="$(_gh_copilot_explain "$BUFFER")"
_prompt_msg "${result:-No explanation found}"
}
zle -N zsh_gh_copilot_suggest
zle -N zsh_gh_copilot_explain