Skip to content

Commit 5571b83

Browse files
committed
fix(complete): Trailing space after zsh directory completions
zsh dynamic completions would add a trailing space after all completions, including directories. This prevented natural path completion. Users had to delete the trailing space before continuing to type or tab-complete within the directory. The fix is for the dynamic completion script to separate completions into directories and non-directories, and providing the directory completions to zsh with a different _describe invocation. For directories: 1. Strip the trailing slash from the completion value 2. Let zsh add it back as a suffix using `_describe -S '/'` 3. Also provide `-r '/'` to make the slash auto-removable when typing '/' This makes dynamic path completions reasonably emulate zsh-native file completions. This matches the behavior already implemented for bash completions which uses 'compopt -o nospace' to achieve the same result. Fixes: #6178 Ref: jj-vcs/jj#5582
1 parent 06a2311 commit 5571b83

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

clap_complete/src/env/shells.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,25 @@ function _clap_dynamic_completer_NAME() {
381381
)}")
382382
383383
if [[ -n $completions ]]; then
384-
_describe 'values' completions
384+
local -a dirs=()
385+
local -a other=()
386+
local completion
387+
for completion in $completions; do
388+
local value="${completion%%:*}"
389+
if [[ "$value" == */ ]]; then
390+
local dir_no_slash="${value%/}"
391+
if [[ "$completion" == *:* ]]; then
392+
local desc="${completion#*:}"
393+
dirs+=("$dir_no_slash:$desc")
394+
else
395+
dirs+=("$dir_no_slash")
396+
fi
397+
else
398+
other+=("$completion")
399+
fi
400+
done
401+
[[ -n $dirs ]] && _describe 'values' dirs -S '/' -r '/'
402+
[[ -n $other ]] && _describe 'values' other
385403
fi
386404
}
387405

clap_complete/tests/snapshots/home/dynamic-env/exhaustive/zsh/zsh/_exhaustive

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,25 @@ function _clap_dynamic_completer_exhaustive() {
1111
)}")
1212

1313
if [[ -n $completions ]]; then
14-
_describe 'values' completions
14+
local -a dirs=()
15+
local -a other=()
16+
local completion
17+
for completion in $completions; do
18+
local value="${completion%%:*}"
19+
if [[ "$value" == */ ]]; then
20+
local dir_no_slash="${value%/}"
21+
if [[ "$completion" == *:* ]]; then
22+
local desc="${completion#*:}"
23+
dirs+=("$dir_no_slash:$desc")
24+
else
25+
dirs+=("$dir_no_slash")
26+
fi
27+
else
28+
other+=("$completion")
29+
fi
30+
done
31+
[[ -n $dirs ]] && _describe 'values' dirs -S '/' -r '/'
32+
[[ -n $other ]] && _describe 'values' other
1533
fi
1634
}
1735

clap_complete/tests/testsuite/zsh.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,32 @@ help -- Print this message or the help of the giv
370370
let actual = runtime.complete(input, &term).unwrap();
371371
assert_data_eq!(actual, expected);
372372
}
373+
374+
#[test]
375+
#[cfg(all(unix, feature = "unstable-dynamic"))]
376+
#[cfg(feature = "unstable-shell-tests")]
377+
fn complete_dynamic_dir_no_trailing_space() {
378+
if !common::has_command(CMD) {
379+
return;
380+
}
381+
382+
let term = completest::Term::new();
383+
let mut runtime = common::load_runtime::<RuntimeBuilder>("dynamic-env", "exhaustive");
384+
385+
// First, complete to the directory name with slash.
386+
// A trailing slash should not be added after the slash.
387+
let input = "exhaustive hint --file tes\t\t";
388+
let expected = snapbox::str!["% exhaustive hint --file tests/"];
389+
let actual = runtime.complete(input, &term).unwrap();
390+
assert_data_eq!(actual, expected);
391+
392+
// Verify hitting tab again shows the directory contents.
393+
// This only works if there is no trailing space after the slash.
394+
let input = "exhaustive hint --file tests/\t\t";
395+
let expected = snapbox::str![[r#"
396+
% exhaustive hint --file tests/
397+
tests/examples.rs tests/snapshots tests/testsuite
398+
"#]];
399+
let actual = runtime.complete(input, &term).unwrap();
400+
assert_data_eq!(actual, expected);
401+
}

0 commit comments

Comments
 (0)