Rust · 6618 bytes Raw Blame History
1 use crate::cli::{Hook, Shell};
2
3 use super::Result;
4
5 /// Generate shell integration code.
6 pub fn run(shell: Shell, cmd: String, hook: Hook, no_cmd: bool) -> Result<()> {
7 let output = match shell {
8 Shell::Bash => generate_bash(&cmd, &hook, no_cmd),
9 Shell::Zsh => generate_zsh(&cmd, &hook, no_cmd),
10 Shell::Fish => generate_fish(&cmd, &hook, no_cmd),
11 };
12
13 println!("{}", output);
14 Ok(())
15 }
16
17 fn generate_bash(cmd: &str, hook: &Hook, no_cmd: bool) -> String {
18 let mut output = String::new();
19
20 // Hook function
21 output.push_str(r#"
22 # gump hook - called after directory changes
23 __gump_hook() {
24 command gump add -- "$PWD"
25 }
26 "#);
27
28 // Hook trigger based on type
29 match hook {
30 Hook::Prompt => {
31 output.push_str(r#"
32 # Update on every prompt
33 if [[ "${PROMPT_COMMAND:-}" != *'__gump_hook'* ]]; then
34 PROMPT_COMMAND="__gump_hook;${PROMPT_COMMAND#;}"
35 fi
36 "#);
37 }
38 Hook::Pwd => {
39 output.push_str(r#"
40 # Update when directory changes
41 __gump_oldpwd="$PWD"
42 __gump_pwd_hook() {
43 if [[ "$PWD" != "$__gump_oldpwd" ]]; then
44 __gump_oldpwd="$PWD"
45 __gump_hook
46 fi
47 }
48 if [[ "${PROMPT_COMMAND:-}" != *'__gump_pwd_hook'* ]]; then
49 PROMPT_COMMAND="__gump_pwd_hook;${PROMPT_COMMAND#;}"
50 fi
51 "#);
52 }
53 }
54
55 // Command aliases (unless --no-cmd)
56 if !no_cmd {
57 output.push_str(&format!(
58 r#"
59 # Jump function
60 {cmd}() {{
61 local result
62 result=$(command gump query -- "$@")
63 if [[ -n "$result" ]]; then
64 builtin cd -- "$result" && __gump_hook
65 else
66 echo "gump: no match found" >&2
67 return 1
68 fi
69 }}
70
71 # Interactive mode with fzf
72 {cmd}i() {{
73 local result
74 result=$(command gump query --all -- "$@" | fzf --height=40% --reverse)
75 if [[ -n "$result" ]]; then
76 builtin cd -- "$result" && __gump_hook
77 fi
78 }}
79 "#,
80 cmd = cmd
81 ));
82 }
83
84 // The magic: command_not_found_handle for no-prefix jumping
85 output.push_str(r#"
86 # No-prefix directory jumping
87 command_not_found_handle() {
88 # Check if it's a local directory first
89 if [[ -d "$1" ]]; then
90 builtin cd -- "$1" && __gump_hook
91 return 0
92 fi
93
94 # Query gump database
95 local result
96 result=$(command gump query -- "$@" 2>/dev/null)
97 if [[ -n "$result" ]]; then
98 builtin cd -- "$result" && __gump_hook
99 return 0
100 fi
101
102 # Fallback to default "command not found" behavior
103 echo "bash: $1: command not found" >&2
104 return 127
105 }
106 "#);
107
108 output
109 }
110
111 fn generate_zsh(cmd: &str, hook: &Hook, no_cmd: bool) -> String {
112 let mut output = String::new();
113
114 // Hook function
115 output.push_str(r#"
116 # gump hook - called after directory changes
117 __gump_hook() {
118 command gump add -- "$PWD"
119 }
120 "#);
121
122 // Hook trigger
123 match hook {
124 Hook::Prompt => {
125 output.push_str(r#"
126 # Update on every prompt
127 [[ -n "${precmd_functions[(r)__gump_hook]}" ]] || precmd_functions+=(__gump_hook)
128 "#);
129 }
130 Hook::Pwd => {
131 output.push_str(r#"
132 # Update when directory changes (chpwd hook)
133 [[ -n "${chpwd_functions[(r)__gump_hook]}" ]] || chpwd_functions+=(__gump_hook)
134 "#);
135 }
136 }
137
138 // Command aliases
139 if !no_cmd {
140 output.push_str(&format!(
141 r#"
142 # Jump function
143 {cmd}() {{
144 local result
145 result=$(command gump query -- "$@")
146 if [[ -n "$result" ]]; then
147 builtin cd -- "$result"
148 else
149 echo "gump: no match found" >&2
150 return 1
151 fi
152 }}
153
154 # Interactive mode with fzf
155 {cmd}i() {{
156 local result
157 result=$(command gump query --all -- "$@" | fzf --height=40% --reverse)
158 if [[ -n "$result" ]]; then
159 builtin cd -- "$result"
160 fi
161 }}
162 "#,
163 cmd = cmd
164 ));
165 }
166
167 // ZLE widget for no-prefix jumping
168 output.push_str(r#"
169 # ZLE widget to intercept commands before execution
170 __gump_accept_line() {
171 local first_word="${BUFFER%% *}"
172
173 # Skip if buffer is empty
174 if [[ -z "$BUFFER" ]]; then
175 zle .accept-line
176 return
177 fi
178
179 # Skip paths (starting with . / or ~)
180 if [[ "$first_word" == "."* ]] || \
181 [[ "$first_word" == "/"* ]] || \
182 [[ "$first_word" == "~"* ]]; then
183 zle .accept-line
184 return
185 fi
186
187 # Skip if command exists (whence checks builtins, functions, aliases, and PATH)
188 if whence "$first_word" >/dev/null 2>&1; then
189 zle .accept-line
190 return
191 fi
192
193 # Check if it's a local directory
194 if [[ -d "$first_word" ]]; then
195 BUFFER="cd ${(q)first_word}"
196 zle .accept-line
197 return
198 fi
199
200 # Query gump database
201 local result
202 result=$(command gump query -- $=BUFFER 2>/dev/null)
203 if [[ -n "$result" ]]; then
204 BUFFER="cd ${(q)result}"
205 fi
206
207 zle .accept-line
208 }
209
210 zle -N accept-line __gump_accept_line
211 "#);
212
213 output
214 }
215
216 fn generate_fish(cmd: &str, hook: &Hook, no_cmd: bool) -> String {
217 let mut output = String::new();
218
219 // Hook function based on type
220 match hook {
221 Hook::Prompt => {
222 output.push_str(r#"
223 # Update on every prompt
224 function __gump_hook --on-event fish_prompt
225 command gump add -- $PWD
226 end
227 "#);
228 }
229 Hook::Pwd => {
230 output.push_str(r#"
231 # Update when directory changes
232 function __gump_hook --on-variable PWD
233 command gump add -- $PWD
234 end
235 "#);
236 }
237 }
238
239 // Command aliases
240 if !no_cmd {
241 output.push_str(&format!(
242 r#"
243 # Jump function
244 function {cmd} --description "Jump to a directory"
245 set -l result (command gump query -- $argv)
246 if test -n "$result"
247 cd $result
248 else
249 echo "gump: no match found" >&2
250 return 1
251 end
252 end
253
254 # Interactive mode with fzf
255 function {cmd}i --description "Jump to a directory (interactive)"
256 set -l result (command gump query --all -- $argv | fzf --height=40% --reverse)
257 if test -n "$result"
258 cd $result
259 end
260 end
261 "#,
262 cmd = cmd
263 ));
264 }
265
266 // fish_command_not_found for no-prefix jumping
267 output.push_str(r#"
268 # No-prefix directory jumping - must erase existing handler first
269 functions -e fish_command_not_found
270
271 function fish_command_not_found --on-event fish_command_not_found
272 # Check if it's a local directory first
273 if test -d "$argv[1]"
274 cd $argv[1]
275 return 0
276 end
277
278 # Query gump database
279 set -l result (command gump query -- $argv 2>/dev/null)
280 if test -n "$result"
281 cd $result
282 return 0
283 end
284
285 # Fallback to default handler
286 __fish_default_command_not_found_handler $argv
287 end
288 "#);
289
290 output
291 }
292