diff --git a/cmd/wut/cmd_init.go b/cmd/wut/cmd_init.go index e1a64a0..7c5ec84 100644 --- a/cmd/wut/cmd_init.go +++ b/cmd/wut/cmd_init.go @@ -116,12 +116,15 @@ _wut_completions() { fi } -complete -F _wut_completions wut - # zsh completion if [[ -n ${ZSH_VERSION-} ]]; then + if (( ! $+functions[compdef] || ! $+parameters[_comps] )); then + autoload -U +X compinit && compinit + fi autoload -U +X bashcompinit && bashcompinit -fi` +fi + +complete -F _wut_completions wut` fmt.Println(wrapper) } diff --git a/cmd/wut/cmd_init_test.go b/cmd/wut/cmd_init_test.go new file mode 100644 index 0000000..ee5d68d --- /dev/null +++ b/cmd/wut/cmd_init_test.go @@ -0,0 +1,65 @@ +package main + +import ( + "io" + "os" + "strings" + "testing" +) + +func TestCmdInitLoadsZshBashCompletionBeforeRegisteringCompletion(t *testing.T) { + output := captureStdout(t, cmdInit) + + compdefCheck := strings.Index(output, "if (( ! $+functions[compdef] || ! $+parameters[_comps] )); then") + compinitSetup := strings.Index(output, " autoload -U +X compinit && compinit") + bashcompinitSetup := strings.Index(output, "autoload -U +X bashcompinit && bashcompinit") + registerCompletion := strings.Index(output, "complete -F _wut_completions wut") + + if compdefCheck == -1 { + t.Fatal("init output must check whether compdef and _comps are already available") + } + if compinitSetup == -1 { + t.Fatal("init output is missing zsh compinit setup") + } + if bashcompinitSetup == -1 { + t.Fatal("init output is missing zsh bashcompinit setup") + } + if registerCompletion == -1 { + t.Fatal("init output is missing completion registration") + } + if compdefCheck > compinitSetup { + t.Fatal("zsh compdef check must guard compinit setup") + } + if compinitSetup > bashcompinitSetup { + t.Fatal("zsh compinit setup must run before bashcompinit setup") + } + if bashcompinitSetup > registerCompletion { + t.Fatal("zsh bashcompinit setup must run before completion registration") + } +} + +func captureStdout(t *testing.T, fn func()) string { + t.Helper() + + originalStdout := os.Stdout + reader, writer, err := os.Pipe() + if err != nil { + t.Fatalf("create stdout pipe: %v", err) + } + defer func() { + os.Stdout = originalStdout + }() + + os.Stdout = writer + fn() + if err := writer.Close(); err != nil { + t.Fatalf("close stdout writer: %v", err) + } + + output, err := io.ReadAll(reader) + if err != nil { + t.Fatalf("read stdout: %v", err) + } + + return string(output) +}