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
58 changes: 58 additions & 0 deletions cmd/openai/image_picker_completion_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"net/http"
"net/http/httptest"
"os"
"strings"
"sync/atomic"
"testing"
)

func TestMainPickerCompletion(t *testing.T) {
var requests atomic.Int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
requests.Add(1)
w.WriteHeader(http.StatusInternalServerError)
}))
defer server.Close()
home := t.TempDir()
env := []string{"HOME=" + home, "USERPROFILE=" + home, "XDG_CONFIG_HOME=" + home, "OPENAI_BASE_URL=" + server.URL}
for _, shell := range []string{"bash", "zsh", "fish", "pwsh"} {
t.Run(shell, func(t *testing.T) {
standard := runMainDispatchWithEnv(t, shell, env, "openai", "@completion", shell)
if standard.code != 0 || standard.stderr != "" || standard.stdout == "" || strings.Contains(standard.stdout, "picker_disable") {
t.Fatalf("standard completion = %+v", standard)
}
disabled := runMainDispatchWithEnv(t, shell, env, "openai", "@completion", shell, "--picker=false")
if disabled != standard {
t.Fatalf("explicit false changed ordinary completion: %+v", disabled)
}
for _, format := range []string{"text", "json"} {
got := runMainDispatchWithEnv(t, shell, env, "openai", "--format-error", format, "@completion", shell, "--picker")
if shell == "pwsh" {
if got.code != 1 || got.stdout != "" {
t.Fatalf("unsupported hook = %+v", got)
}
message := got.stderr
if format == "json" {
payload := decodeMainStructuredError(t, format, got.stderr)
message, _ = payload["message"].(string)
}
if !strings.Contains(message, "press Enter") {
t.Fatalf("missing Enter fallback: %q", message)
}
} else if got.code != 0 || got.stderr != "" || !strings.HasPrefix(got.stdout, standard.stdout+"\n") || !strings.Contains(got.stdout, "openai_picker_disable") {
t.Fatalf("picker completion = %+v", got)
}
}
})
}
if requests.Load() != 0 {
t.Fatalf("completion made %d API requests", requests.Load())
}
entries, err := os.ReadDir(home)
if err != nil || len(entries) != 0 {
t.Fatalf("completion wrote to the home/config directory: %v, %v", entries, err)
}
}
3 changes: 3 additions & 0 deletions docs/image-generation-saving.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ requesting an image, and Ctrl+C exits. Command previews and Ctrl+P support Bash,
zsh, fish and PowerShell 7. In other or unidentified shells, generation remains
available, but command printing is disabled with an explanation in the picker.

An optional [Tab shortcut](image-picker-shortcuts.md) opens the same picker in
Bash 4.3+, zsh and fish. Other shells use Enter.

After a successful generation, the picker reopens below the saved result with
the active prompt and settings. Choices last until this invocation exits.
Explicit image flags, output formats, piped input and redirected output retain
Expand Down
35 changes: 35 additions & 0 deletions docs/image-picker-shortcuts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Image picker shortcuts

`openai images generate` opens the image picker when you press Enter in an
interactive terminal. To also open it with Tab, load the optional hook in your
current shell session:

```bash
# Bash 4.3 or newer
source <(openai @completion bash --picker)
```

```zsh
# zsh, after completion has been initialized
autoload -Uz compinit && compinit
source <(openai @completion zsh --picker)
```

```fish
# fish
openai @completion fish --picker | source
```

Type `openai images generate` and press Tab at the end of the line. Ctrl+C
leaves the picker and keeps the command editable. Other command lines retain
ordinary completion. The shortcut requires `openai` on PATH as an executable;
aliases, shell functions, extra arguments, redirections and compound commands
use ordinary completion. Existing custom Bash Tab bindings are preserved.

Run `openai_picker_disable` to turn off the hook in this session. Closing the
shell also removes it. These commands do not edit startup files. Ordinary
completion scripts are still available without `--picker`.

PowerShell and Bash older than 4.3 keep normal Tab completion. Press Enter after
`openai images generate` to open the picker. Hooks are inactive outside an
interactive terminal or when `TERM=dumb`.
7 changes: 7 additions & 0 deletions internal/autocomplete/autocomplete.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ func OutputCompletionScript(ctx context.Context, cmd *cli.Command) error {
if err != nil {
return cli.Exit(err, 1)
}
if cmd.Bool("picker") {
picker, err := renderPickerCompletion(s, cmd.Root().Name)
if err != nil {
return cli.Exit(err, 1)
}
completionScript += "\n" + picker
}

_, err = cmd.Writer.Write([]byte(completionScript))
if err != nil {
Expand Down
Loading
Loading