Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cli/assets/completions/_usage
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ _usage_usage_cache_policy() {
}

_usage() {
emulate -L zsh
typeset -A opt_args
local curcontext="$curcontext" cache_policy

Expand Down
77 changes: 77 additions & 0 deletions cli/tests/shell_completions_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,83 @@ _testcli
let _ = fs::remove_dir_all(&temp_dir);
}

/// Regression test for user zsh options leaking into generated completions.
///
/// With KSH_ARRAYS enabled, zsh arrays become zero-indexed. The generated
/// completion script indexes tab-split completion rows as zsh arrays, so it
/// must enter local zsh emulation before parsing `complete-word` output.
#[test]
fn test_zsh_completion_ignores_ksh_arrays_option() {
if skip_if_shell_missing("zsh") {
return;
}

let usage_bin = build_usage_binary();
let temp_dir =
env::temp_dir().join(format!("usage_zsh_ksh_arrays_test_{}", std::process::id()));
fs::create_dir_all(&temp_dir).unwrap();

let spec = r#"
bin "testcli"
cmd "doctor" help="Check installation"
"#;
let spec_kdl_file = temp_dir.join("testcli.kdl");
fs::write(&spec_kdl_file, spec).unwrap();

let gen = Command::new(&usage_bin)
.args(["generate", "completion", "zsh", "testcli", "-f"])
.arg(spec_kdl_file.to_str().unwrap())
.output()
.expect("Failed to generate zsh completion");
let comp_file = temp_dir.join("_testcli");
fs::write(&comp_file, &gen.stdout).unwrap();

let test_script = format!(
r#"#!/usr/bin/env zsh
set -e
setopt KSH_ARRAYS
export PATH="{usage_dir}:$PATH"
export TMPDIR="{tmp}"

autoload -U compinit
compinit -u
source {comp}

compadd() {{
print -r -- "[compadd:inserts] ${{inserts[*]}}"
}}

words=(testcli d)
CURRENT=2
_testcli
"#,
usage_dir = usage_bin.parent().unwrap().to_str().unwrap(),
tmp = temp_dir.to_str().unwrap(),
comp = comp_file.to_str().unwrap(),
);

let script_file = temp_dir.join("test.zsh");
fs::write(&script_file, &test_script).unwrap();

let result = Command::new("zsh")
.arg(script_file.to_str().unwrap())
.output()
.expect("Failed to run zsh test");
let stdout = String::from_utf8_lossy(&result.stdout);
let stderr = String::from_utf8_lossy(&result.stderr);
assert!(
result.status.success(),
"zsh completion script exited non-zero ({}).\nstdout:\n{stdout}\nstderr:\n{stderr}",
result.status
);
assert!(
stdout.contains("[compadd:inserts] doctor"),
"Expected only the insert value in compadd inserts.\nstdout:\n{stdout}\nstderr:\n{stderr}"
);

let _ = fs::remove_dir_all(&temp_dir);
}

#[test]
fn test_powershell_completion_integration() {
if skip_if_shell_missing("pwsh") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ _usage_mycli_cache_policy() {
}

_mycli() {
emulate -L zsh
typeset -A opt_args
local curcontext="$curcontext" cache_policy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ expression: "complete_zsh(&CompleteOptions\n{\n usage_bin: \"usage\".to_strin
local curcontext="$curcontext"

_mycli() {
emulate -L zsh
typeset -A opt_args
local curcontext="$curcontext" cache_policy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ _usage_mycli_cache_policy() {
}

_mycli() {
emulate -L zsh
typeset -A opt_args
local curcontext="$curcontext" cache_policy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ expression: "complete_zsh_init(\"usage\")"
# on $PATH whose first line is a `usage` shebang.

_usage_default_complete() {
emulate -L zsh
local cmd cmdpath
cmd="${words[1]}"
if [[ "$cmd" == */* ]]; then
Expand Down
2 changes: 2 additions & 0 deletions lib/src/complete/zsh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ _usage_{bin_snake}_cache_policy() {{
out.push(format!(
r#"
_{bin_snake}() {{
emulate -L zsh
typeset -A opt_args
local curcontext="$curcontext" cache_policy

Expand Down Expand Up @@ -187,6 +188,7 @@ pub fn complete_zsh_init(usage_bin: &str) -> String {
# on $PATH whose first line is a `usage` shebang.

_usage_default_complete() {{
emulate -L zsh
local cmd cmdpath
cmd="${{words[1]}}"
if [[ "$cmd" == */* ]]; then
Expand Down